Rotate Tick Labels in Matplotlib: 45 and 90 Degrees

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
Three Matplotlib bar charts side by side with x-axis month labels at 0, 45 and 90 degrees rotation
At 0 the month names collide. 45 is the usual compromise; 90 is fully vertical.

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
Command Prompt showing four different Matplotlib methods all reporting a tick label rotation of 45 degrees
Four routes, one result: every label object reports a rotation of 45.
CallUse 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.
Command Prompt showing the Matplotlib UserWarning that set_ticklabels should only be used with a fixed number of ticks
The fix is one line: call 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")
Two Matplotlib bar charts comparing rotated labels with default alignment against labels with ha right and rotation mode anchor
Left: rotation only. Right: the same rotation, anchored to each tick.

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:

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'.

Leave a Comment