Pyplot set tick frequency and tick labels - python

I'm trying to make a plot with matplotlib where I want to specify both the position of the tick marks, and the text of the tick marks. I can individually do both with yticks(np.arange(0,1.1,1/16.)) and gca().set_yticklabels(['1','2','3']). However, for some reason when I do both of them together, the labels do not appear on the graph. Is there a reason for this? How can I get around it? Below is a working example of what I want to accomplish.
x = [-1, -0.2, -0.15, 0.15, 0.2, 7.8, 7.85, 8.15, 8.2, 12]
y = [1, 1, 15/16., 15/16., 1, 1, 15/16., 15/16., 1, 1]
figure(1)
plot(x,y)
xlabel('Time (years)')
ylabel('Brightness')
yticks(np.arange(0,1.1,1/16.))
xticks(np.arange(0,13,2))
ylim(12/16.,16.5/16.)
xlim(-1,12)
gca().set_yticklabels(['12/16', '13/16', '14/16', '15/16', '16/16'])
show(block = False)
Effectively I just wanted to replace the numerical values with fractions, but when I run this, the labels do not appear. It seems that using both yticks() and set_yticklabels together is a problem because if I remove either line, the remaining line works as it should.
If anyone can indicate how to simply force the label to be a fraction, that would also solve my problem.
EDIT:
I found an ugly workaround by using
ylim(12/16., 16.5/16)
gca().yaxis.set_major_locator(FixedLocator([12/16., 13/16., 14/16., 15/16., 16/16.]))
gca().yaxis.set_major_formatter(FixedFormatter(['12/16', '13/16', '14/16', '15/16', '16/16']))
While this may work for this specific example, it does not generalize well and it is cumbersome to specify the exact location and label of every tick mark. If anyone finds another solution, I'm all ears.

1) Your arange should produce 5 ticks, the same as labels you set.
arange is not good for that. It is better to use linspace.
2) You can set ticks and labels with the same function
plot(x,y)
xlabel('Time (years)')
ylabel('Brightness')
yticks(np.linspace(12/16., 1, 5), ('12/16', '13/16', '14/16', '15/16', '16/16') )
xticks(np.arange(0,13,2))
ylim(12/16.,16.5/16.)
xlim(-1,12)
3) Note that you should adjust the actual values of the axis with the position of the labels using linspace(12/16., 1, 5) instead of arange(0, 1.1, 1/16.))

Related

Giving imshow a custom list of yaxis labels

I'm trying to give pyplot.imshow() a list of values that aren't necessarily linear to use as y-axis labels. The truncated list is:
run_numbers = array([815676, 815766, 815767, 815768, 815769, 815770, 815771, 815772,
815773, 815774, 815775, 815776, 815777, 815778, 815779, 815780,
815781, 815783, 815784, 815785, 815786, 815789, 815790, 815792,
815793, 815794, 815795, 815796, 815797, 815798, 815799, 815800,
815801, 815802, 815803, 815804, 815805, 815806, 815807, 815808,
815809, 815811, 815812, 815813, 815814, 815815, 815816, 815817,
815818, 815819, 815820, 815821, 815822, 815823, 815824, 815825,
815826, 815827, 815829, 815830, 815831, 815832, 815833, 815834,
815835, 815836, 815837, 815838, 815839, 815841, 815842, 815843,
815844, 815845, 815846, 815847, 815848, 815849, 815851, 815852,
815853, 815854, 815855, 815856, 815857, 815858, 815859, 815860,
815861, 815863, 815864, 815865, 815866, 815867, 815869, 815870,
815871, 815872, 815873, 815874, 815875, 815876, 815877, 815878])
My image looks like this:
At first glance, this seems fine. But imshow isn't using the values from the list, instead it's using a linear range from 815676 to the max value of the list. I've tried a few different things:
plt.imshow(np.array(profiles), aspect='auto', vmin=-5, vmax=20, extent=[0,500,max(run_numbers), min(run_numbers)])
The above code gave the image above, which makes sense given what I put in extent.
Is there a way to tell imshow to use the values in the list as the yaxis label? I've tried ax.yticklabels and ax.ytick, but those also give a linear progression of numbers instead of the list values.
Please let me know how I can clarify my question if there's any confusion. I can also provide an example data set if my question isn't clear.
Letting the extent go between 0 and the number of labels minus 1, and then use plt.yticks(range(N), run_numbers) would set the labels. As there are about 100 labels, this would look very crowded, which could be mitigated by setting a large figure size and a small font. Or the labels could be set every with steps, e.g. steps of 10:
import numpy as np
import matplotlib.pyplot as plt
run_numbers = np.array([815676, 815766, 815767, 815768, 815769, 815770, 815771, 815772, 815773, 815774, 815775, 815776, 815777, 815778, 815779, 815780, 815781, 815783, 815784, 815785, 815786, 815789, 815790, 815792, 815793, 815794, 815795, 815796, 815797, 815798, 815799, 815800, 815801, 815802, 815803, 815804, 815805, 815806, 815807, 815808, 815809, 815811, 815812, 815813, 815814, 815815, 815816, 815817, 815818, 815819, 815820, 815821, 815822, 815823, 815824, 815825, 815826, 815827, 815829, 815830, 815831, 815832, 815833, 815834, 815835, 815836, 815837, 815838, 815839, 815841, 815842, 815843, 815844, 815845, 815846, 815847, 815848, 815849, 815851, 815852, 815853, 815854, 815855, 815856, 815857, 815858, 815859, 815860, 815861, 815863, 815864, 815865, 815866, 815867, 815869, 815870, 815871, 815872, 815873, 815874, 815875, 815876, 815877, 815878])
N = len(run_numbers)
profiles = np.random.randn(N, 501).cumsum(axis=1)
plt.imshow(profiles, aspect='auto', extent=[0, 500, N-1, 0])
plt.yticks(range(0, N, 10), run_numbers[::10])
plt.show()
With
plt.figure(figsize=(10, 16))
plt.imshow(profiles, aspect='auto', extent=[0, 500, N-1, 0])
plt.yticks(range(N), run_numbers, fontsize=8)
It could look like

Map boolean values to strings

I am plotting a graph where my x variable is 'Mg' and my y variable is 'Si'. I have a third variable called 'binary'. If binary is equal to 0 or 1, how do I colour the plotted point in red or black respectively?
I need to use the functions plt.scatter and colourbar(). I've read about colourbar but it seems to generate a continuous spectrum of colour. I've tried using plt.colors.from_levels_and_colors instead but I'm not really sure how to use it properly.
levels = [0,1]
colors = ['r','b']
cmap, norm = plt.colors.from_levels_and_colors(levels, colors)
plt.scatter(data_train['Mg'], data_train['Si'], c = data_train['binary'])
plt.show()
Also, in the future, instead of asking a question like this in this forum what can I do to solve the problem on my own? I try to read the documentation online first but often find it hard to understand.
np.where makes encoding binary values easy.
np.where([1, 0, 0, 1], 'yes', 'no')
# array(['yes', 'no', 'no', 'yes'], dtype='<U3')
colors = np.where(data_train['binary'], 'black', 'red')
plt.scatter(data_train['Mg'], data_train['Si'], c=colors)
If you're working with multiple "quantitive" colors, not with colormap, you probably should change your c from binary to mpl-friedly format. I.e.
point_colors = [colors[binary] for binary in data_train['binary']]
plt.scatter(data_train['Mg'], data_train['Si'], c=point_colors)

Python matplotlib - setting x-axis scale

I have this graph displaying the following:
plt.plot(valueX, scoreList)
plt.xlabel("Score number") # Text for X-Axis
plt.ylabel("Score") # Text for Y-Axis
plt.title("Scores for the topic "+progressDisplay.topicName)
plt.show()
valueX = [1, 2, 3, 4] and
scoreList = [5, 0, 0, 2]
I want the scale to go up in 1's, no matter what values are in 'scoreList'. Currently get my x-axis going up in .5 instead of 1s.
How do I set it so it goes up only in 1?
Just set the xticks yourself.
plt.xticks([1,2,3,4])
or
plt.xticks(valueX)
Since the range functions happens to work with integers you could use that instead:
plt.xticks(range(1, 5))
Or be even more dynamic and calculate it from the data:
plt.xticks(range(min(valueX), max(valueX)+1))
Below is my favorite way to set the scale of axes:
plt.xlim(-0.02, 0.05)
plt.ylim(-0.04, 0.04)
Hey it looks like you need to set the x axis scale.
Try
matplotlib.axes.Axes.set_xscale(1, 'linear')
Here's the documentation for that function

How to set the value of the axis multiplier in matplotlib?

I have to plot values in the range (0, 32000). When I do, I get the following tick labels:
0, 5000, 10000, 15000, 20000, 25000, 30000, 35000
but I would like to have the following:
0, 5, 10, 15, 20, 25, 30, 35
with the axis multiplier (that small number just below the tick labels) x 10^3. I really need x 10^3.
I know how to force matplotlib to use an axis multiplier. When I use the following:
fmt = ScalarFormatter()
fmt.set_powerlimits((-3, 3))
ax.xaxis.set_major_formatter(fmt)
or this:
pylab.ticklabel_format(axis='x', style='sci', scilimits=(-3, 3),
useOffset=False)
matplotlib always returns with the axis multiplier x 10^4, so I get these ugly tick labels:
0.0, 0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 3.5
You will agree that the multiplier x 10^3 results in a lot nicer tick labels. How do I set 10^3 instead of 10^4?
This question is similar but does not concern setting a concrete value for the axis multiplier.
From the available tickers, two options are:
EngFormatter()
FuncFormatter()
Using EngFormatter you get the SI prefixes, and optionally a unit by passing unit='Hz' for example.
More generally, you can define your own formatter
scale_factor = 10**3
fmt = mpl.ticker.FuncFormatter(lambda x, pos: '{0:g}'.format(x/scale_factor))
Edit:
You could also sub-class ScalarFormatter and override the order of magnitude determination, as in:
class MagnitudeFormatter(matplotlib.ticker.ScalarFormatter):
def __init__(self, exponent=None):
super().__init__()
self._fixed_exponent = exponent
def _set_order_of_magnitude(self):
if self._fixed_exponent:
self.orderOfMagnitude = self._fixed_exponent
else:
super()._set_order_of_magnitude()
Then using MagnitudeFormatter(3) as your formatter. This has the benefit in that it retains the "1e3" axis decoration.
This may not be the answer you are looking for, but you can specify a list of axis tick titles. For instance instead of plotting every tick from 1-10 you could make a list like ['1', '', '3', '', '5', '', '7', '', '9'] would only show a tick label on every other tick. This means so long as your domain you are plotting over doesn't change you can specify the exact strings you want displayed. If you would like I can post some code where I did this.

python matplotlib dash-dot-dot - how to?

I am using python and matplotlib to generate graphical output.
Is there a simple way to generate a dash-dot-dot line-style?
I am aware of the '--', '-.', and ':' options. Unfortunately, '-..' does not result in a dash-dot-dot line.
I have looked at the set_dashes command, but that seems to control the length of the dashes and the space between two adjacent dashes.
One option may be to plot two lines on top of each other; one dashed with ample space between the dashes - and one dotted, with the dots as large as the dashes are wide and spaced so that two dots are in between each of the dashes. I do not doubt this can be done, I am simply hoping for an easier way.
Did I overlook an option?
You can define custom dashes:
import matplotlib.pyplot as plt
line, = plt.plot([1,5,2,4], '-')
line.set_dashes([8, 4, 2, 4, 2, 4])
plt.show()
[8, 4, 2, 4, 2, 4] means
8 points on, (dash)
4 points off,
2 points on, (dot)
4 points off,
2 points on, (dot)
4 points off.
#Achim noted you can also specify the dashes parameter:
plt.plot([1,5,2,4], '-', dashes=[8, 4, 2, 4, 2, 4])
plt.show()
produces the same result shown above.

Categories

Resources