Reputation: 9929
I am trying to set x tick labels, but allowing matplotlib to decide where to place the ticks. Unfortunately, when I set the x tick labels, matplotlib will use the label strings only for the ticks that have been displayed.
Example:
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(111)
xs = range(26)
ys = range(26)
ax.plot(xs, ys)
ax.set_xticklabels(list('abcdefghijklmnopqrstuvwxyz'))
plt.show()
This gives:
This for me is quite unintuitive; I would expect the labels to correspond to the points that they are showing. (I can however understand this behavior is more useful when plotting multiple series which do not use the same x values.)
NB: I do not want all 26 letters as ticks. Rather, I want just the 6 (as matplotlib has suggested), but those 6 should be appropriate for their positions. This should then make it easy to change the fig size without having to manually recalculate the new tick labels.
How could I achieve the desired result?
Upvotes: 6
Views: 10378
Reputation: 9929
I think the most appropriate solution is to use FuncFormatter
which will allow you to look up the tick label value you need, and allow matplotlib to determine positions and number of ticks:
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter, MaxNLocator
fig = plt.figure()
ax = fig.add_subplot(111)
xs = range(26)
ys = range(26)
labels = list('abcdefghijklmnopqrstuvwxyz')
def format_fn(tick_val, tick_pos):
if int(tick_val) in xs:
return labels[int(tick_val)]
else:
return ''
ax.xaxis.set_major_formatter(FuncFormatter(format_fn))
ax.xaxis.set_major_locator(MaxNLocator(integer=True))
ax.plot(xs, ys)
plt.show()
This will give you:
(Thanks Daniel Lenz for the pointers and other suggestions).
NB:
As mentioned by tcaswell, xaxis.set_major_locator(MaxNLocator(integer=True))
is used to ensure ticks are placed at integer values only.
Upvotes: 3
Reputation: 3877
You need to assign the tick location prior to the labels. The easiest way to get the desired result for your example would be to add ax.set_xticks(xs)
before you set the tick labels.
import matplotlib.pyplot as pl
fig = pl.figure()
ax = fig.add_subplot(111)
xs = range(26)
ys = range(26)
_ = ax.plot(xs, ys)
alph = list('abcdefghijklmnopqrstuvwxyz')
ax.set_xticks(xs)
ax.set_xticklabels(alph)
pl.savefig('ticklabels.png', dpi=300)
Edit: I'd suggest the following to address your wish for a more general solution:
from matplotlib.ticker import MaxNLocator
import matplotlib.pyplot as pl
import numpy as np
alph = list('abcdefghijklmnopqrstuvwxyz')
nticks = 8
fig, ax = pl.subplots()
xs = np.linspace(-10., 10., 100)
ys = np.polyval([1., 2., 3.], xs)
_ = ax.plot(xs, ys)
ax.xaxis.set_major_locator(MaxNLocator(nticks))
ax.set_xticklabels(alph[::int(len(alph)/nticks)])
pl.savefig('ticklabels.png', dpi=300)
You specify the number of ticks with the MaxNLocator and map them to the alphabet via slicing.
I'm not sure about how reliable MaxNLocator is in returning exactly the desired number of ticks, though. As mentioned in the comments, you could simply use len(ax.get_xticks())
to get the number of ticks.
Upvotes: 5