Matplotlib log scale tick label number formatting - python

With matplotlib when a log scale is specified for an axis, the default method of labeling that axis is with numbers that are 10 to a power eg. 10^6. Is there an easy way to change all of these labels to be their full numerical representation? eg. 1, 10, 100, etc.
Note that I do not know what the range of powers will be and want to support an arbitrary range (negatives included).

Sure, just change the formatter.
For example, if we have this plot:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.axis([1, 10000, 1, 100000])
ax.loglog()
plt.show()
You could set the tick labels manually, but then the tick locations and labels would be fixed when you zoom/pan/etc. Therefore, it's best to change the formatter. By default, a logarithmic scale uses a LogFormatter, which will format the values in scientific notation. To change the formatter to the default for linear axes (ScalarFormatter) use e.g.
from matplotlib.ticker import ScalarFormatter
for axis in [ax.xaxis, ax.yaxis]:
axis.set_major_formatter(ScalarFormatter())

I've found that using ScalarFormatter is great if all your tick values are greater than or equal to 1. However, if you have a tick at a number <1, the ScalarFormatter prints the tick label as 0.
We can use a FuncFormatter from the matplotlib ticker module to fix this issue. The simplest way to do this is with a lambda function and the g format specifier (thanks to #lenz in comments).
import matplotlib.ticker as ticker
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda y, _: '{:g}'.format(y)))
Note in my original answer I didn't use the g format, instead I came up with this lambda function with FuncFormatter to set numbers >= 1 to their integer value, and numbers <1 to their decimal value, with the minimum number of decimal places required (i.e. 0.1, 0.01, 0.001, etc). It assumes that you are only setting ticks on the base10 values.
import matplotlib.ticker as ticker
import numpy as np
ax.yaxis.set_major_formatter(ticker.FuncFormatter(lambda y,pos: ('{{:.{:1d}f}}'.format(int(np.maximum(-np.log10(y),0)))).format(y)))
For clarity, here's that lambda function written out in a more verbose, but also more understandable, way:
def myLogFormat(y,pos):
# Find the number of decimal places required
decimalplaces = int(np.maximum(-np.log10(y),0)) # =0 for numbers >=1
# Insert that number into a format string
formatstring = '{{:.{:1d}f}}'.format(decimalplaces)
# Return the formatted tick label
return formatstring.format(y)
ax.yaxis.set_major_formatter(ticker.FuncFormatter(myLogFormat))

I found Joe's and Tom's answers very helpful, but there are a lot of useful details in the comments on those answers. Here's a summary of the two scenarios:
Ranges above 1
Here's the example code like Joe's, but with a higher range:
import matplotlib.pyplot as plt
fig, ax = plt.subplots()
ax.axis([1, 10000, 1, 1000000])
ax.loglog()
plt.show()
That shows a plot like this, using scientific notation:
As in Joe's answer, I use a ScalarFormatter, but I also call set_scientific(False). That's necessary when the scale goes up to 1000000 or above.
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
fig, ax = plt.subplots()
ax.axis([1, 10000, 1, 1000000])
ax.loglog()
for axis in [ax.xaxis, ax.yaxis]:
formatter = ScalarFormatter()
formatter.set_scientific(False)
axis.set_major_formatter(formatter)
plt.show()
Ranges below 1
As in Tom's answer, here's what happens when the range goes below 1:
import matplotlib.pyplot as plt
from matplotlib.ticker import ScalarFormatter
fig, ax = plt.subplots()
ax.axis([0.01, 10000, 1, 1000000])
ax.loglog()
for axis in [ax.xaxis, ax.yaxis]:
formatter = ScalarFormatter()
formatter.set_scientific(False)
axis.set_major_formatter(formatter)
plt.show()
That displays the first two ticks on the x axis as zeroes.
Switching to a FuncFormatter handles that. Again, I had problems with numbers 1000000 or higher, but adding a precision to the format string solved it.
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter
fig, ax = plt.subplots()
ax.axis([0.01, 10000, 1, 1000000])
ax.loglog()
for axis in [ax.xaxis, ax.yaxis]:
formatter = FuncFormatter(lambda y, _: '{:.16g}'.format(y))
axis.set_major_formatter(formatter)
plt.show()

regarding these questions
What if I wanted to change the numbers to, 1, 5, 10, 20?
– aloha Jul 10 '15 at 13:26
I would like to add ticks in between, like 50,200, etc.., How can I do
that? I tried, set_xticks[50.0,200.0] but that doesn't seem to work!
– ThePredator Aug 3 '15 at 12:54
But with ax.axis([1, 100, 1, 100]), ScalarFormatter gives 1.0, 10.0, ... which is not what I desire. I want it to give integers...
– CPBL Dec 7 '15 at 20:22
you can solve those issue like this with MINOR formatter:
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter("%.8f"))
ax.set_yticks([0.00000025, 0.00000015, 0.00000035])
in my application I'm using this format scheme, which I think solves most issues related to log scalar formatting; the same could be done for data > 1.0 or x axis formatting:
plt.ylabel('LOGARITHMIC PRICE SCALE')
plt.yscale('log')
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter("%.8f"))
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter("%.8f"))
#####################################################
#force 'autoscale'
#####################################################
yd = [] #matrix of y values from all lines on plot
for n in range(len(plt.gca().get_lines())):
line = plt.gca().get_lines()[n]
yd.append((line.get_ydata()).tolist())
yd = [item for sublist in yd for item in sublist]
ymin, ymax = np.min(yd), np.max(yd)
ax.set_ylim([0.9*ymin, 1.1*ymax])
#####################################################
z = []
for i in [0.0000001, 0.00000015, 0.00000025, 0.00000035,
0.000001, 0.0000015, 0.0000025, 0.0000035,
0.00001, 0.000015, 0.000025, 0.000035,
0.0001, 0.00015, 0.00025, 0.00035,
0.001, 0.0015, 0.0025, 0.0035,
0.01, 0.015, 0.025, 0.035,
0.1, 0.15, 0.25, 0.35]:
if ymin<i<ymax:
z.append(i)
ax.set_yticks(z)
for comments on "force autoscale" see: Python matplotlib logarithmic autoscale
which yields:
then to create a general use machine:
# user controls
#####################################################
sub_ticks = [10,11,12,14,16,18,22,25,35,45] # fill these midpoints
sub_range = [-8,8] # from 100000000 to 0.000000001
format = "%.8f" # standard float string formatting
# set scalar and string format floats
#####################################################
ax.yaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_major_formatter(matplotlib.ticker.FormatStrFormatter(format))
ax.yaxis.set_minor_formatter(matplotlib.ticker.ScalarFormatter())
ax.yaxis.set_minor_formatter(matplotlib.ticker.FormatStrFormatter(format))
#force 'autoscale'
#####################################################
yd = [] #matrix of y values from all lines on plot
for n in range(len(plt.gca().get_lines())):
line = plt.gca().get_lines()[n]
yd.append((line.get_ydata()).tolist())
yd = [item for sublist in yd for item in sublist]
ymin, ymax = np.min(yd), np.max(yd)
ax.set_ylim([0.9*ymin, 1.1*ymax])
# add sub minor ticks
#####################################################
set_sub_formatter=[]
for i in sub_ticks:
for j in range(sub_range[0],sub_range[1]):
set_sub_formatter.append(i*10**j)
k = []
for l in set_sub_formatter:
if ymin<l<ymax:
k.append(l)
ax.set_yticks(k)
#####################################################
yields:

The machinery outlined in the accepted answer works great, but sometimes a simple manual override is easier. To get ticks at 1, 10, 100, 1000, for example, you could say:
ticks = 10**np.arange(4)
plt.xticks(ticks, ticks)
Note that it is critical to specify both the locations and the labels, otherwise matplotlib will ignore you.
This mechanism can be used to obtain arbitrary formatting. For instance:
plt.xticks(ticks, [ f"{x:.0f}" for x in ticks ])
or
plt.xticks(ticks, [ f"10^{int(np.log10(x))}" for x in ticks ])
or
plt.xticks(ticks, [ romannumerals(x) for x in ticks ])
(where romannumerals is an imagined function that converts its argument into Roman numerals).
As an aside, this technique also works if you want ticks at arbitrary intervals, e.g.,
ticks = [1, 2, 5, 10, 20, 50, 100]
etc.

import matplotlib.pyplot as plt
plt.rcParams['axes.formatter.min_exponent'] = 2
plt.xlim(1e-5, 1e5)
plt.loglog()
plt.show()
This will become default for all plots in a session.
See also: LogFormatter tickmarks scientific format limits

Related

Set matplotlib tick locators, but specify spacing around them

For example if I have the following:
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
fig, ax = plt.subplots()
ax.set_xlim(left=0, right=11)
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
Which looks as:
Here the range is set to include 0 and 11 so that there's some spacing around the plotted values, but the data only contains values of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - so I'd like to not have 0 and 11 on the xaxis.
Looking at the documentation for ticker.MultipleLocator (here) it's not clear how this should be done (of if it can be with a locator). I tried to use the view_limits method but it just seems to return a tuple.
Ideally the values 0 and 11 would be gone, and the plot would look as:
edit 1
The following "works"
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
fig, ax = plt.subplots()
ax.set_xlim(left=1e-2, right=11 - 1e-2)
ax.xaxis.set_major_locator(ticker.MultipleLocator(1))
Though removing fractional amounts in order to not display something isn't a solution here as it's constrained to the base value of the MultipleLocator.
For example - what if I wanted the following:
ax.set_xlim(left=1e-2, right=11 - 1e-2)
ax.xaxis.set_major_locator(ticker.MultipleLocator(0.5))
Which looks as:
Then I have 10.5 and so on, whereas I might still like the spacing to be a particular value not limited to the MultipleLocator size.
Getting the lower limit is something that is a bit tricky and not by default included in any of the Matplotlib ticker. However, taking a look at the source code
(https://github.com/matplotlib/matplotlib/blob/v3.5.1/lib/matplotlib/ticker.py#L2734-L2751) gives a good hint how to implement this by deriving a new MyMultipleLocator class from MultipleLocator by yourself. Here is a piece of code that should work:
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, _Edge_integer
import numpy as np
class MyMultipleLocator(MultipleLocator):
def __init__(self, base=1.0, offset=0.):
self._edge = _Edge_integer(base, 0)
self._offset = offset
def tick_values(self, vmin, vmax):
# we HAVE to re-implement this method as it is called by
# xaxis.set_major_locator(...)
vmin = self._edge.ge(vmin)
step = self._edge.step
n = (vmax - vmin + 0.001 * step) // step
locs = self._offset + vmin - step + np.arange(n + 3) * step
return self.raise_if_exceeds(locs)
fig, ax = plt.subplots()
x = np.arange(1, 11)
ax.plot(x, np.random.randint(-3, 3, size=x.size))
tick_spacing = 2
ax.xaxis.set_major_locator(MyMultipleLocator(base=tick_spacing, offset=0))
You can now additionally change the offset manually in the last line. You could even do this automatically by checking if the minimum (the nearest int, that is) is an odd or an even number, but if I understand the question correctly then this is not required here.
Output from the above code:
example 1
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(MyMultipleLocator(base=1, offset=0))
ax.set_xlim(0.5, 10.5)
Which gives:
example 2
fig, ax = plt.subplots()
ax.xaxis.set_major_locator(MyMultipleLocator(base=2, offset=0.5))
ax.set_xlim(0., 11)
Which gives:

pyplot xticklabel (date/time) sometimes off by a minute

I have a program that uses matplotlib.pyplot to produce a couple of graphs. On the x-axis I want to label the major ticks with a 4-hour time interval (so: 00:00, 04:00, 08:00 etc.)
When I plot the graph, the first few labels are okay but the rest isn't.
The code that I'm using (not showing the ax3 being set-up and loading of the data as this is IMHO off-topic):
import matplotlib as mpl
mpl.use("Agg") # activate Anti-Grain Geometry library
import matplotlib.pyplot as plt
import numpy as nmp
:
:
hours = mpl.dates.HourLocator()
fourhours = 4. / 24.
# [DAY]
major_ticks = nmp.arange(nmp.ceil(DY[1, 0]/fourhours)*fourhours, DY[-1, 0], fourhours)
ax3.set_xlabel('past day')
ax3.grid(True)
ax3.set_ylim([Ymin, Ymax])
ax3.set_xlim([DY[1, 0], DY[-1, 0]])
#
t = nmp.array(DY[:, 0]) # date/time
ax3.set_xticklabels(t, size='small')
ax3.set_yticklabels([])
ax3.set_xticks(major_ticks)
ax3.xaxis.set_major_formatter(mpl.dates.DateFormatter('%R'))
ax3.grid(which='major', alpha=0.5)
ax3.xaxis.set_minor_locator(hours)
ax3.grid(which='minor', alpha=0.2)
#
s = nmp.array(DY[:, 2]) # averages
slo = nmp.array(DY[:, 1]) # minima
shi = nmp.array(DY[:, 3]) # maxima
line, = ax3.plot(t, s, marker='.', linestyle='', color='red', lw=2)
ax3.fill_between(t, slo, shi, interpolate=True, color='red', alpha=0.2)
DY[1,0] contains the value 736364.444444
DY[-1,0] is 736365.458333
and major_ticks then becomes:
[ 736364.5 736364.66666667 736364.83333333 736365. 736365.16666667 736365.33333333]
This all looks fine to me but the resulting graph doesn't:
Any suggestions on how to fix this are welcome.
#j-p-petersen proposed to use linspace:
I replaced the line that calculates major_ticks = ... with this code:
intervals = int((DY[-1, 0] - DY[1, 0]) / fourhours) + 1
major_ticks = np.linspace(np.ceil(DY[1, 0]/fourhours)*fourhours, np.floor(DY[-1, 0]/fourhours)*fourhours, intervals)
This reduced the problem but still some ticks show as one minute before the hour i.s.o. on the hour.
I think this is due to a rounding error in the floating point arithmetic in here:
fourhours = 4. / 24.
Instead of using arange you could use linspace, this way it will not undershoot the target.
Alternatively you could do the calculation using timedelta and deltatime, and then afterwards convert it the Matplotlib's number dates with matplotlib.dates.date2num.

Remove axis scale

I've spent some time fruitlessly searching for an answer to my question, so I think a new question is in order. Consider this plot:
The axes labels use scientific notation. On the y-axis, all is well. However, I have tried and failed to get rid off the scaling factor that Python added in the lower-right corner. I would like to either remove this factor completely and simply indicate it by the units in the axis title or have it multiplied to every tick label. Everything would look better than this ugly 1e14.
Here's the code:
import numpy as np data_a = np.loadtxt('exercise_2a.txt')
import matplotlib as mpl
font = {'family' : 'serif',
'size' : 12}
mpl.rc('font', **font)
import matplotlib.pyplot as plt
fig = plt.figure()
subplot = fig.add_subplot(1,1,1)
subplot.plot(data_a[:,0], data_a[:,1], label='$T(t)$', linewidth=2)
subplot.set_yscale('log')
subplot.set_xlabel("$t[10^{14}s]$",fontsize=14)
subplot.set_ylabel("$T\,[K]$",fontsize=14)
plt.xlim(right=max(data_a [:,0]))
plt.legend(loc='upper right')
plt.savefig('T(t).pdf', bbox_inches='tight')
Update: Incorporating Will's implementation of scientificNotation into my script, the plot now looks like
Much nicer if you ask me. Here's the complete code for anyone wanting to adopt some part of it:
import numpy as np
data = np.loadtxt('file.txt')
import matplotlib as mpl
font = {'family' : 'serif',
'size' : 16}
mpl.rc('font', **font)
import matplotlib.pyplot as plt
fig = plt.figure()
subplot = fig.add_subplot(1,1,1)
subplot.plot(data[:,0], data[:,1], label='$T(t)$', linewidth=2)
subplot.set_yscale('log')
subplot.set_xlabel("$t[s]$",fontsize=20)
subplot.set_ylabel("$T\,[K]$",fontsize=20)
plt.xlim(right=max(data [:,0]))
plt.legend(loc='upper right')
def scientificNotation(value):
if value == 0:
return '0'
else:
e = np.log10(np.abs(value))
m = np.sign(value) * 10 ** (e - int(e))
return r'${:.0f} \cdot 10^{{{:d}}}$'.format(m, int(e))
formatter = mpl.ticker.FuncFormatter(lambda x, p: scientificNotation(x))
plt.gca().xaxis.set_major_formatter(formatter)
plt.savefig('T(t).pdf', bbox_inches='tight', transparent=True)
Just divide the x-values by 1e14:
subplot.plot(data_a[:,0] / 1e14, data_a[:,1], label='$T(t)$', linewidth=2)
If you want to add the label to each individual tick, you'll have to provide a custom formatter, like in tom's answer.
If you want it to look like as nice as the ticks on your y-axis, you could provide a function to format it with LaTeX:
def scientificNotation(value):
if value == 0:
return '0'
else:
e = np.log10(np.abs(value))
m = np.sign(value) * 10 ** (e - int(e))
return r'${:.0f} \times 10^{{{:d}}}$'.format(m, int(e))
# x is the tick value; p is the position on the axes.
formatter = mpl.ticker.FuncFormatter(lambda x, p: scientificNotation(x))
plt.gca().xaxis.set_major_formatter(formatter)
Of course, this will clutter your x-axis up quite a bit, so you might end up needing to display them at an angle, for example.
You can also change the tick formatter with the ticker module.
An example would be to use a FormatStrFormatter:
import matplotlib.pyplot as plt
import matplotlib.ticker as ticker
fig,ax = plt.subplots()
ax.semilogy(np.linspace(0,5e14,50),np.logspace(3,7,50),'b-')
ax.xaxis.set_major_formatter(ticker.FormatStrFormatter('%.0e'))
Also see the answers here with lots of good ideas for ways to solve this.
In addition to the good answer from Will Vousden, you can set what you write in your ticks with:
plt.xticks(range(6), range(6))
the first range(6) is the location and the second is the label.

Specify format of floats for tick labels

I am trying to set the format to two decimal numbers in a matplotlib subplot environment. Unfortunately, I do not have any idea how to solve this task.
To prevent using scientific notation on the y-axis I used ScalarFormatter(useOffset=False) as you can see in my snippet below. I think my task should be solved by passing further options/arguments to the used formatter. However, I could not find any hint in matplotlib's documentation.
How can I set two decimal digits or none (both cases are needed)? I am not able to provide sample data, unfortunately.
-- SNIPPET --
f, axarr = plt.subplots(3, sharex=True)
data = conv_air
x = range(0, len(data))
axarr[0].scatter(x, data)
axarr[0].set_ylabel('$T_\mathrm{air,2,2}$', size=FONT_SIZE)
axarr[0].yaxis.set_major_locator(MaxNLocator(5))
axarr[0].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[0].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[0].grid(which='major', alpha=0.5)
axarr[0].grid(which='minor', alpha=0.2)
data = conv_dryer
x = range(0, len(data))
axarr[1].scatter(x, data)
axarr[1].set_ylabel('$T_\mathrm{dryer,2,2}$', size=FONT_SIZE)
axarr[1].yaxis.set_major_locator(MaxNLocator(5))
axarr[1].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[1].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[1].grid(which='major', alpha=0.5)
axarr[1].grid(which='minor', alpha=0.2)
data = conv_lambda
x = range(0, len(data))
axarr[2].scatter(x, data)
axarr[2].set_xlabel('Iterationsschritte', size=FONT_SIZE)
axarr[2].xaxis.set_major_locator(MaxNLocator(integer=True))
axarr[2].set_ylabel('$\lambda$', size=FONT_SIZE)
axarr[2].yaxis.set_major_formatter(ScalarFormatter(useOffset=False))
axarr[2].yaxis.set_major_locator(MaxNLocator(5))
axarr[2].tick_params(direction='out', labelsize=FONT_SIZE)
axarr[2].grid(which='major', alpha=0.5)
axarr[2].grid(which='minor', alpha=0.2)
See the relevant documentation in general and specifically
from matplotlib.ticker import FormatStrFormatter
fig, ax = plt.subplots()
ax.yaxis.set_major_formatter(FormatStrFormatter('%.2f'))
If you are directly working with matplotlib's pyplot (plt) and if you are more familiar with the new-style format string, you can try this:
from matplotlib.ticker import StrMethodFormatter
plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.0f}')) # No decimal places
plt.gca().yaxis.set_major_formatter(StrMethodFormatter('{x:,.2f}')) # 2 decimal places
From the documentation:
class matplotlib.ticker.StrMethodFormatter(fmt)
Use a new-style format string (as used by str.format()) to format the
tick.
The field used for the value must be labeled x and the field used for
the position must be labeled pos.
The answer above is probably the correct way to do it, but didn't work for me.
The hacky way that solved it for me was the following:
ax = <whatever your plot is>
# get the current labels
labels = [item.get_text() for item in ax.get_xticklabels()]
# Beat them into submission and set them back again
ax.set_xticklabels([str(round(float(label), 2)) for label in labels])
# Show the plot, and go home to family
plt.show()
format labels using lambda function
3x the same plot with differnt y-labeling
Minimal example
import numpy as np
import matplotlib as mpl
import matplotlib.pylab as plt
from matplotlib.ticker import FormatStrFormatter
fig, axs = mpl.pylab.subplots(1, 3)
xs = np.arange(10)
ys = 1 + xs ** 2 * 1e-3
axs[0].set_title('default y-labeling')
axs[0].scatter(xs, ys)
axs[1].set_title('custom y-labeling')
axs[1].scatter(xs, ys)
axs[2].set_title('x, pos arguments')
axs[2].scatter(xs, ys)
fmt = lambda x, pos: '1+ {:.0f}e-3'.format((x-1)*1e3, pos)
axs[1].yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fmt))
fmt = lambda x, pos: 'x={:f}\npos={:f}'.format(x, pos)
axs[2].yaxis.set_major_formatter(mpl.ticker.FuncFormatter(fmt))
You can also use 'real'-functions instead of lambdas, of course.
https://matplotlib.org/3.1.1/gallery/ticks_and_spines/tick-formatters.html
In matplotlib 3.1, you can also use ticklabel_format. To prevents scientific notation without offsets:
plt.gca().ticklabel_format(axis='both', style='plain', useOffset=False)

Python matplotlib restrict to integer tick locations

Quite often I want to make a bar chart of counts. If the counts are low I often get major and/or minor tick locations that are not integers. How can I prevent this? It makes no sense to have a tick at 1.5 when the data are counts.
This is my first attempt:
import pylab
pylab.figure()
ax = pylab.subplot(2, 2, 1)
pylab.bar(range(1,4), range(1,4), align='center')
major_tick_locs = ax.yaxis.get_majorticklocs()
if len(major_tick_locs) < 2 or major_tick_locs[1] - major_tick_locs[0] < 1:
ax.yaxis.set_major_locator(pylab.MultipleLocator(1))
minor_tick_locs = ax.yaxis.get_minorticklocs()
if len(minor_tick_locs) < 2 or minor_tick_locs[1] - minor_tick_locs[0] < 1:
ax.yaxis.set_minor_locator(pylab.MultipleLocator(1))
which works OK when the counts are small but when they are large, I get many many minor ticks:
import pylab
ax = pylab.subplot(2, 2, 2)
pylab.bar(range(1,4), range(100,400,100), align='center')
major_tick_locs = ax.yaxis.get_majorticklocs()
if len(major_tick_locs) < 2 or major_tick_locs[1] - major_tick_locs[0] < 1:
ax.yaxis.set_major_locator(pylab.MultipleLocator(1))
minor_tick_locs = ax.yaxis.get_minorticklocs()
if len(minor_tick_locs) < 2 or minor_tick_locs[1] - minor_tick_locs[0] < 1:
ax.yaxis.set_minor_locator(pylab.MultipleLocator(1))
How can I get the desired behaviour from the first example with small counts whilst avoiding what happens in the second?
You can use the MaxNLocator method, like so:
from pylab import MaxNLocator
ya = axes.get_yaxis()
ya.set_major_locator(MaxNLocator(integer=True))
I had a similar issue with a histogram I was plotting showing fractional count. Here's how I was able to resolve it:
plt.hist(x=[Dataset being counted])
# Get your current y-ticks (loc is an array of your current y-tick elements)
loc, labels = plt.yticks()
# This sets your y-ticks to the specified range at whole number intervals
plt.yticks(np.arange(0, max(loc), step=1))
I think it turns out I can just ignore the minor ticks. I'm going to give this a go and see if it stands up in all use cases:
def ticks_restrict_to_integer(axis):
"""Restrict the ticks on the given axis to be at least integer,
that is no half ticks at 1.5 for example.
"""
from matplotlib.ticker import MultipleLocator
major_tick_locs = axis.get_majorticklocs()
if len(major_tick_locs) < 2 or major_tick_locs[1] - major_tick_locs[0] < 1:
axis.set_major_locator(MultipleLocator(1))
def _test_restrict_to_integer():
pylab.figure()
ax = pylab.subplot(1, 2, 1)
pylab.bar(range(1,4), range(1,4), align='center')
ticks_restrict_to_integer(ax.xaxis)
ticks_restrict_to_integer(ax.yaxis)
ax = pylab.subplot(1, 2, 2)
pylab.bar(range(1,4), range(100,400,100), align='center')
ticks_restrict_to_integer(ax.xaxis)
ticks_restrict_to_integer(ax.yaxis)
_test_restrict_to_integer()
pylab.show()
pylab.bar(range(1,4), range(1,4), align='center')
and
xticks(range(1,40),range(1,40))
has worked in my code.
Just use the align optional parameter and xticks does the magic.

Categories

Resources