Aligning rotated xticklabels with their respective xticks
Check the x axis of the figure below. How can I move the labels a bit to the left so that they align with their respective ticks?
I'm rotating the labels using:
ax.set_xticks(xlabels_positions)
ax.set_xticklabels(xlabels, rotation=45)
But, as you can see, the rotation is centered on the middle of the text labels. Which makes it look like they are shifted to the right.
I've tried using this instead:
ax.set_xticklabels(xlabels, rotation=45, rotation_mode="anchor")
... but it doesn't do what I wished for. And "anchor"
seems to be the only value allowed for the rotation_mode
parameter.
Solution 1:
You can set the horizontal alignment of ticklabels, see the example below. If you imagine a rectangular box around the rotated label, which side of the rectangle do you want to be aligned with the tickpoint?
Given your description, you want: ha='right'
n=5
x = np.arange(n)
y = np.sin(np.linspace(-3,3,n))
xlabels = ['Ticklabel %i' % i for i in range(n)]
fig, axs = plt.subplots(1,3, figsize=(12,3))
ha = ['right', 'center', 'left']
for n, ax in enumerate(axs):
ax.plot(x,y, 'o-')
ax.set_title(ha[n])
ax.set_xticks(x)
ax.set_xticklabels(xlabels, rotation=40, ha=ha[n])
Solution 2:
Rotating the labels is certainly possible. Note though that doing so reduces the readability of the text. One alternative is to alternate label positions using a code like this:
import numpy as np
n=5
x = np.arange(n)
y = np.sin(np.linspace(-3,3,n))
xlabels = ['Long ticklabel %i' % i for i in range(n)]
fig, ax = plt.subplots()
ax.plot(x,y, 'o-')
ax.set_xticks(x)
labels = ax.set_xticklabels(xlabels)
for i, label in enumerate(labels):
label.set_y(label.get_position()[1] - (i % 2) * 0.075)
For more background and alternatives, see this post on my blog
Solution 3:
The accepted ha='right'
is still not enough to visually align labels with ticks:
- if
rotation=45
, then addrotation_mode='anchor'
- otherwise add a
ScaledTranslation
rotation_mode='anchor'
If the rotation angle is roughly 45 deg, then setting rotation_mode='anchor'
is simplest:
x = xticks = np.arange(10)
xticklabels = (f'xticklabel {tick}' for tick in xticks)
fig, ax = plt.subplots()
ax.bar(x, x - 4.5)
ax.set_xticks(xticks)
# since rotation=45, anchor the rotation to fix label/tick alignment
ax.set_xticklabels(xticklabels, rotation=45, ha='right', rotation_mode='anchor')
However this doesn't work well for other angles like 70 deg (see left), in which case we need to apply a ScaledTranslation
(see right):
ScaledTranslation
If the rotation angle is more extreme or you just want more fine-grained control, then apply a ScaledTranslation
as described in how to move a tick's label in matplotlib:
...
ax.set_xticklabels(xticklabels, rotation=70, ha='right')
# create offset transform (x=5pt)
from matplotlib.transforms import ScaledTranslation
dx, dy = 5, 0
offset = ScaledTranslation(dx/fig.dpi, dy/fig.dpi, scale_trans=fig.dpi_scale_trans)
# apply offset transform to all xticklabels
for label in ax.xaxis.get_majorticklabels():
label.set_transform(label.get_transform() + offset)
Solution 4:
An easy, loop-free alternative is to use the horizontalalignment
Text property as a keyword argument to xticks
[1]. In the below, at the commented line, I've forced the xticks
alignment to be "right".
n=5
x = np.arange(n)
y = np.sin(np.linspace(-3,3,n))
xlabels = ['Long ticklabel %i' % i for i in range(n)]
fig, ax = plt.subplots()
ax.plot(x,y, 'o-')
plt.xticks(
[0,1,2,3,4],
["this label extends way past the figure's left boundary",
"bad motorfinger", "green", "in the age of octopus diplomacy", "x"],
rotation=45,
horizontalalignment="right") # here
plt.show()
(yticks
already aligns the right edge with the tick by default, but for xticks
the default appears to be "center".)
[1] You find that described in the xticks documentation if you search for the phrase "Text properties".
Solution 5:
If you dont want to modify the xtick labels, you can just use:
plt.xticks(rotation=45)