To rotate x-axis tick labels in Matplotlib, call plt.xticks(rotation=45) after plotting. That one line fixes the overlapping month names almost everyone lands here for.
plt.xticks(rotation=45) # or 90 for vertical
ax.tick_params(axis="x", labelrotation=45) # the axes-object version
Rotation alone usually isn’t enough, though. At 45 degrees the labels drift away from their ticks unless you also set the alignment, and that’s the part most guides leave out.
Every figure below came out of Matplotlib 3.11.2 on Python 3.12.5.
Rotate tick labels 45 and 90 degrees
Here is the same bar chart three times, so you can see what each angle buys you:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
months = ["January", "February", "March", "April", "May", "June"]
sales = [120, 145, 132, 168, 151, 180]
fig, ax = plt.subplots(figsize=(6, 3))
ax.bar(months, sales)
plt.xticks(rotation=45) # the one-liner most people want
fig.tight_layout()
fig.savefig("rotated.png")
plt.close(fig)
print("labels rotated by 45 degrees and saved")
Output:
labels rotated by 45 degrees and saved
45 degrees keeps the labels readable while saving horizontal space. 90 is worth it when the names are long or the bars are narrow.
There’s no rule here, it’s a readability call. If the labels still collide at 45, go to 90 before you start shrinking the font.
tight_layout() matters here. Rotated labels stick out below the axes, and without it they’ll be cut off when you save.
Four ways to rotate Matplotlib tick labels
They all reach the same place, and which you pick depends on whether you’re holding an axes object. Here’s each one reporting back what it did:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
months = ["January", "February", "March", "April", "May", "June"]
sales = [120, 145, 132, 168, 151, 180]
# 1. plt.xticks on the current axes
fig, ax = plt.subplots()
ax.bar(months, sales); plt.xticks(rotation=45)
print("plt.xticks ->", ax.get_xticklabels()[0].get_rotation())
plt.close(fig)
# 2. tick_params, which works on a specific axes object
fig, ax = plt.subplots()
ax.bar(months, sales); ax.tick_params(axis="x", labelrotation=45)
print("ax.tick_params ->", ax.get_xticklabels()[0].get_rotation())
plt.close(fig)
# 3. setp on the label objects
fig, ax = plt.subplots()
ax.bar(months, sales); plt.setp(ax.get_xticklabels(), rotation=45)
print("plt.setp ->", ax.get_xticklabels()[0].get_rotation())
plt.close(fig)
# 4. set_xticklabels, which needs the ticks fixed first
fig, ax = plt.subplots()
ax.bar(months, sales)
ax.set_xticks(range(len(months))) # fix the locator...
ax.set_xticklabels(months, rotation=45) # ...then the labels
print("set_xticklabels ->", ax.get_xticklabels()[0].get_rotation())
plt.close(fig)
Output:
plt.xticks -> 45.0
ax.tick_params -> 45.0
plt.setp -> 45.0
set_xticklabels -> 45.0
| Call | Use it when |
|---|---|
plt.xticks(rotation=45) | Quick scripts, one chart, current axes |
ax.tick_params(axis='x', labelrotation=45) | You have an axes object, or several |
plt.setp(ax.get_xticklabels(), rotation=45) | You want to set alignment at the same time |
ax.set_xticklabels(labels, rotation=45) | You are replacing the label text too |
tick_params is the one I reach for in anything larger than a script, because it belongs to the axes rather than to whatever was plotted last. It has plenty of other uses, covered in the tick_params guide.
Why set_xticklabels warns you
ax.set_xticklabels() is the method that bites. Call it without fixing the ticks first and Matplotlib tells you it cannot guarantee the labels line up:
import warnings
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
months = ["Jan", "Feb", "Mar", "Apr"]
fig, ax = plt.subplots()
ax.plot(months, [1, 2, 3, 4])
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
ax.set_xticklabels(months, rotation=45) # no set_xticks() first
for w in caught:
print(f"{w.category.__name__}: {w.message}")
plt.close(fig)
What Matplotlib says:
UserWarning: set_ticklabels() should only be used with a fixed number of ticks, i.e. after set_ticks() or using a FixedLocator. Otherwise, ticks may be mislabeled.
ax.set_xticks() before ax.set_xticklabels().The locator decides where the ticks go, the formatter decides what they say. Set labels without fixing positions and the two can drift apart, which is exactly what the warning is about.
Older Matplotlib phrased this as FixedFormatter should only be used together with FixedLocator. Same problem, same one-line fix, and it’s why set_xticklabels deserves care.
Aligning rotated labels with ha and rotation_mode
This is the part that separates a tidy chart from a nearly-right one. Rotating a label spins it about its centre, so the end of the text drifts away from the tick it belongs to.
ha="right" plus rotation_mode="anchor" pins the end of each label to its own tick:
plt.setp(ax.get_xticklabels(), rotation=45, ha="right", rotation_mode="anchor")
Follow the dotted guide down from each tick. On the left the label straddles it; on the right the end of the text stops exactly on it.
You don’t need it at 90 degrees, because a vertical label is already centred on its tick.
Rotating date tick labels
Dates are the most common reason for rotating anything, and Matplotlib has a shortcut that does the whole job:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import datetime as dt
days = [dt.date(2026, 1, 1) + dt.timedelta(days=i * 7) for i in range(10)]
values = [3, 5, 4, 7, 6, 8, 7, 9, 8, 11]
fig, ax = plt.subplots(figsize=(6, 3))
ax.plot(days, values, marker="o")
fig.autofmt_xdate(rotation=45) # rotates, right-aligns and makes room, in one call
fig.savefig("dates.png")
plt.close(fig)
print("autofmt_xdate handled rotation, alignment and spacing together")
Output:
autofmt_xdate handled rotation, alignment and spacing together
fig.autofmt_xdate() rotates the labels, right-aligns them and makes room at the bottom in one call. Use it instead of doing the three things by hand.
Rotating labels on every subplot
plt.xticks() only touches the current axes, which is why it appears to do nothing on a grid of subplots. Loop over the axes instead:
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
months = ["January", "February", "March", "April"]
fig, axes = plt.subplots(1, 3, figsize=(10, 3))
for ax in axes: # plt.xticks only touches the current axes
ax.bar(months, [4, 7, 5, 9])
ax.tick_params(axis="x", labelrotation=45)
rotations = [ax.get_xticklabels()[0].get_rotation() for ax in axes]
print("rotation on each subplot:", rotations)
fig.tight_layout()
plt.close(fig)
Output:
rotation on each subplot: [45.0, 45.0, 45.0]
Each axes reports its own rotation, so nothing was missed. If the rotated labels then collide with the row beneath, tight_layout is the usual cure.
Labels cut off after rotating?
Rotated labels extend below the axes, and the saved file crops them unless you make room:
fig.tight_layout()before saving, which recomputes the margins.fig.savefig(name, bbox_inches="tight"), which trims to the drawn content instead.fig.subplots_adjust(bottom=0.3)when you want to set the space yourself.
The first two cover almost every case. Reach for the third only when you need the same margin across several figures.
More Matplotlib axis and tick guides:
- Matplotlib set_xticklabels
- Matplotlib tick_params
- Remove tick labels and ticks
- Matplotlib x-axis label
- Matplotlib tight_layout
Frequently asked questions
How do I rotate x-axis labels in Matplotlib?
plt.xticks(rotation=45), or ax.tick_params(axis='x', labelrotation=45) when you have an axes object. The arguments are listed in the plt.xticks reference.
How do I make tick labels vertical?
Use rotation=90. A vertical label stays centred on its tick, so no alignment change is needed.
Why are my rotated labels not lined up with the ticks?
Rotation spins each label about its centre. Add ha='right' and rotation_mode='anchor' to pin the end of the text to its tick.
Why does set_xticklabels give a warning about fixed ticks?
You set labels without fixing the tick positions. Call ax.set_xticks() first, then ax.set_xticklabels(). Older versions worded it as a FixedFormatter and FixedLocator warning.
How do I rotate date labels on the x-axis?
fig.autofmt_xdate(rotation=45) rotates, right-aligns and makes room in a single call.
Why does plt.xticks do nothing on my subplots?
It only applies to the current axes. Loop over the axes and call tick_params on each one.
My labels are cut off in the saved image. How do I fix it?
Call fig.tight_layout() before saving, or save with bbox_inches='tight'.
Bijay Kumar is a 13-time Microsoft MVP with more than 18 years in software development, and the founder of Python Guides and TSinfo Technologies. He started out building .NET and SharePoint solutions at HP, TCS and KPIT before moving into Python, machine learning and AI, and he also builds web apps with TypeScript and React. He writes the tutorials here himself, and every example is run before publishing so you see the real output. More about Bijay · Microsoft MVP profile · LinkedIn