Matplotlib·Lesson 9·12 min·0/3 exercises

Ticks and labels

Locators say where, formatters say what — almost every tick problem is one of the two

ax.set_xticksMultipleLocatorStrMethodFormatterDateFormatterax.tick_paramsax.margins

Watch it happen

Play it through, or step back and forth yourself.

20,00040,00060,00080,000100,000locator → whereformatter → what it says
import matplotlib.ticker as mticker

ax.yaxis.set_major_locator(
    mticker.MultipleLocator(20000))

ax.yaxis.set_major_formatter(
    mticker.StrMethodFormatter("{x:,.0f}"))

An axis has two separate jobs, done by two separate objects. A locator decides where the ticks go; a formatter decides what each one says. Almost every tick problem is one or the other, and knowing which halves the work.

The idea

An axis has two jobs, and they belong to two different objects. A locator decides where the ticks go. A formatter decides what each one says. Once you know that, tick problems stop being a search and become a question with two possible answers.

import matplotlib.ticker as mticker

ax.yaxis.set_major_locator(mticker.MultipleLocator(20))
ax.yaxis.set_major_formatter(mticker.StrMethodFormatter("{x:,.0f}"))

Note that these hang off ax.xaxis and ax.yaxis, not off ax — this is one of the few places the Axis/Axes distinction from lesson 1 actually shows up in code you write.

Setting them by hand

ax.set_xticks(range(len(items)), items)   # positions, then labels
ax.set_xticks([0, 3, 6])                  # positions only
ax.set_xticks([])                          # remove them entirely

This does both jobs at once, and it's exactly right when you know precisely what you want — seven days, five rating levels, four quarters. Its weakness is that it goes stale: the positions are hardcoded, so the day the data gains a row the labels are wrong and nothing says so.

Rotation

Long category labels collide. The standard fix:

ax.set_xticks(range(len(items)), items, rotation=45, ha="right")

ha="right" is not decoration. Rotating text about its centre leaves it drifting sideways from the tick it belongs to; right-aligning pins the end of the word to the mark. Get this wrong and the labels look subtly, unaccountably off.

The better fix is usually to sidestep it: ax.barh() gives every label a full horizontal line and nothing needs rotating at all.

Locators — a rule instead of a list

  • MultipleLocator(20) — a tick every 20.
  • MaxNLocator(5) — about five, placed at round numbers. Usually what you want.
  • LogLocator() — one per decade, for log axes.
  • AutoMinorLocator() — minor ticks between the major ones.

These keep working when the data changes, which hardcoded positions don't.

Formatters

mticker.StrMethodFormatter("{x:,.0f}")     # 30000 -> "30,000"
mticker.PercentFormatter(xmax=1.0)         # 0.256 -> "25.6%"
mticker.FuncFormatter(lambda v, pos: f"₹{v/1e6:.1f}M")

FuncFormatter takes a function of (value, position) and returns a string, which means any formatting you can express in Python is available. Currency, units, ordinals, "2h 30m" — all three lines away.

One specific irritation worth naming: the +1e7 that appears in the corner of an axis with large numbers. That's ScalarFormatter factoring out a common offset. Turn it off with ax.ticklabel_format(useOffset=False), or replace the formatter outright.

Dates

import matplotlib.dates as mdates

ax.xaxis.set_major_locator(mdates.AutoDateLocator())
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d %b"))
fig.autofmt_xdate()      # rotate and right-align, in one call

Dates get their own locator/formatter pair because "a sensible tick" means something different for them — months, quarters, weekdays. Learn fig.autofmt_xdate() before you write the rotation out longhand.

Fewer is better

Ticks are furniture, not data. Five well-spaced ones beat twelve crowded ones. Minor ticks are usually noise. And these two are worth knowing:

ax.tick_params(axis="x", labelsize=9, length=0)   # labels, no marks
ax.margins(x=0)                                   # drop the 5% padding

See it run

The lesson's code, ready to run and to fiddle with.

Putting the kettle on…

Starting up…

Worked example

not graded

Already written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.

tryswapping MultipleLocator for mticker.MaxNLocator(4) and seeing what it chooses.

Press Run — the output appears here.

Your turn

3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Plot delhi and replace the x tick labels with the day names in upper case. Return [t.get_text() for t in ax.get_xticklabels()].

your answer

Plot delhi, then place a y tick every 20 with a locator rather than by hand. Return [int(v) for v in ax.get_yticks()].

your answer

Plot delhi scaled up by 1000, then format the y ticks with thousands separators. Draw the figure with fig.canvas.draw() and return the rendered label texts.

your answer