Matplotlib set_xticklabels: Custom X-Axis Labels

set_xticklabels replaces the text under each x-axis tick in Matplotlib. It changes the words, not where the ticks sit, so the positions have to be settled first:

ax.set_xticks([1, 2, 3, 4])
ax.set_xticklabels(["Q1", "Q2", "Q3", "Q4"])

Skip that first line and Matplotlib warns you, because it can no longer promise the labels line up with the data. There’s a newer one-call form too, further down.

Output and figures below are from Matplotlib 3.11.2 on Python 3.12.5.

Using set_xticklabels on a chart

Fix the positions, then hand over the text. The two lists are matched up in order:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

quarters = [1, 2, 3, 4]
revenue = [180, 210, 195, 260]

fig, ax = plt.subplots(figsize=(6, 3))
ax.bar(quarters, revenue)

ax.set_xticks(quarters)                                   # fix the positions first
ax.set_xticklabels(["Q1", "Q2", "Q3", "Q4"])              # then replace the text

fig.canvas.draw()
print("labels:", [t.get_text() for t in ax.get_xticklabels()])
plt.close(fig)

Output:

labels: ['Q1', 'Q2', 'Q3', 'Q4']
Three Matplotlib bar charts showing default numeric x labels, month names from set_xticklabels, and the same names rotated and right-aligned
Numbers, then names, then names rotated to fit.

Labels are plain strings, so anything goes: names, dates, currency, units. What they cannot do is move a tick.

The warning set_xticklabels gives you

Call it on its own and Matplotlib tells you why that’s risky:

import warnings
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.plot([1, 2, 3, 4], [10, 20, 15, 25])

with warnings.catch_warnings(record=True) as caught:
    warnings.simplefilter("always")
    ax.set_xticklabels(["a", "b", "c", "d"])       # 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
Without fixed positions, the labels can drift away from the data.

The locator normally recalculates ticks as the view changes. If you’ve replaced the text but not pinned the positions, those two can disagree and the chart quietly lies.

Calling set_xticks first installs a fixed locator, which is what silences it. The same warning turns up when rotating tick labels.

set_xticklabels or set_xticks(labels=…)?

Matplotlib 3.5 added a shorter form that sets both at once and never warns:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.bar([1, 2, 3, 4], [180, 210, 195, 260])

# one call, no warning, positions and text stay in step
ax.set_xticks([1, 2, 3, 4], labels=["Q1", "Q2", "Q3", "Q4"])

fig.canvas.draw()
print("positions:", ax.get_xticks())
print("labels   :", [t.get_text() for t in ax.get_xticklabels()])
plt.close(fig)

Output:

positions: [1 2 3 4]
labels   : ['Q1', 'Q2', 'Q3', 'Q4']

Prefer this in new code. It keeps positions and text in one place, and it’s described alongside the other arguments in the set_xticks guide.

set_xticklabels alignment: ha, va and rotation_mode

Rotated labels need an alignment change or they drift away from their ticks. set_xticklabels returns the Text objects, so you can check what you got:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

months = ["January", "February", "March", "April"]
fig, ax = plt.subplots()
ax.bar(range(4), [4, 7, 5, 9])

ax.set_xticks(range(4))
labels = ax.set_xticklabels(months, rotation=45, ha="right",
                            rotation_mode="anchor")

first = labels[0]
print("rotation           :", first.get_rotation())
print("horizontal align   :", first.get_horizontalalignment())
print("vertical align     :", first.get_verticalalignment())
print("rotation mode      :", first.get_rotation_mode())
plt.close(fig)

Output:

rotation           : 45.0
horizontal align   : right
vertical align     : top
rotation mode      : anchor
Command Prompt showing the rotation, horizontal alignment, vertical alignment and rotation mode of a Matplotlib tick label
Every property readable straight off the returned Text object.
ArgumentDoes whatTypical value
rotationAngle in degrees45 or 90
haHorizontal alignment'right' when rotated 45
vaVertical alignment'top' below the axis
rotation_modeRotate then align, or align then rotate'anchor'

rotation_mode="anchor" is the one that actually pins the end of the text to the tick. Without it, ha alone still leaves a small offset.

set_xticklabels font size, colour and weight

Any Text property can be passed straight through, and the older fontdict argument still works:

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.bar(range(3), [5, 8, 6])
ax.set_xticks(range(3))

# keyword arguments go straight to the Text objects
labels = ax.set_xticklabels(["low", "medium", "high"],
                            fontsize=13, color="#0b6bcb",
                            fontweight="bold", fontstyle="italic")

print("size  :", labels[0].get_fontsize())
print("colour:", labels[0].get_color())
print("weight:", labels[0].get_fontweight())

# or the older fontdict argument, which does the same job
ax.set_xticklabels(["a", "b", "c"], fontdict={"fontsize": 9, "color": "green"})
print("after fontdict:", ax.get_xticklabels()[0].get_fontsize())
plt.close(fig)

Output:

size  : 13.0
colour: #0b6bcb
weight: bold
after fontdict: 9.0

Keyword arguments are easier to read than a dictionary, and they’re what the current documentation uses. fontdict is kept for compatibility.

set_xticklabels needs one label per tick

One rule catches everybody: you need exactly as many labels as there are fixed ticks.

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt

fig, ax = plt.subplots()
ax.bar(range(5), [3, 5, 2, 8, 6])
ax.set_xticks(range(5))                     # five positions

ax.set_xticklabels(["a", "b", "c"])         # only three labels

What happens with three labels and five ticks:

Traceback (most recent call last):
  File "C:\pyguides\xticklabels_mismatch.py", line 9, in <module>
    ax.set_xticklabels(["a", "b", "c"])         # only three labels
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\pyguides\venv\Lib\site-packages\matplotlib\axes\_base.py", line 75, in wrapper
    return get_method(self)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\pyguides\venv\Lib\site-packages\matplotlib\axis.py", line 2260, in set_ticklabels
    raise ValueError(
ValueError: The number of FixedLocator locations (5), usually from a call to set_ticks, does not match the number of labels (3).
Command Prompt showing the ValueError raised when the number of labels does not match the number of fixed ticks in Matplotlib
Five ticks, three labels, and an immediate error.

Build both lists from the same source so they cannot drift apart. If you only want to hide some of them, removing tick labels is the cleaner route than padding with empty strings.

Related Matplotlib axis guides:

Frequently asked questions

What does set_xticklabels do?

It replaces the text shown under each x-axis tick without moving the ticks. The arguments are listed in the set_xticklabels reference.

Why does set_xticklabels give a warning?

Because the tick positions are not fixed, so the labels could end up against the wrong values. Call ax.set_xticks() first.

What is the difference between set_xticks and set_xticklabels?

set_xticks decides where the ticks go; set_xticklabels decides what they say. Use set_xticks(pos, labels=...) to do both at once.

How do I rotate the labels and keep them aligned?

ax.set_xticklabels(labels, rotation=45, ha='right', rotation_mode='anchor'). The alignment is what pins each label to its own tick.

How do I change the font size of tick labels?

Pass fontsize directly, or use ax.tick_params(axis='x', labelsize=12) when you are not replacing the text.

Why do I get a ValueError about the number of labels?

You gave fewer or more labels than there are fixed ticks. The two lists have to be the same length.

Can I use set_xticklabels on subplots?

Yes, but call it on each axes object. It applies to one axes only, not the whole figure.