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']
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.
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
| Argument | Does what | Typical value |
|---|---|---|
rotation | Angle in degrees | 45 or 90 |
ha | Horizontal alignment | 'right' when rotated 45 |
va | Vertical alignment | 'top' below the axis |
rotation_mode | Rotate 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).
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:
- Matplotlib set_xticks
- Rotate tick labels 45 and 90 degrees
- Remove tick labels and ticks
- Matplotlib tick_params
- Matplotlib x-axis label
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.
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