Matplotlib Axis Ticks and Formatters: Make Scales Readable
Updated on
Crowded ticks, overlapping dates, and unrounded numbers make plots look noisy and mislead readers. When ticks land randomly, context disappears and trends get buried. The cure is simple: explicitly choose locators (where ticks go) and formatters (how ticks look) so every axis tells a clear story.
Locator vs formatter at a glance
| Task | Tool | Example use |
|---|---|---|
| Set tick spacing by step | MultipleLocator | Every 5 units on x |
| Limit to a max count | MaxNLocator | Keep ≤ 6 ticks on y |
| Date ticks | AutoDateLocator | Auto months/years |
| Custom labels | FuncFormatter | Append units, add suffix |
| Rotate labels | tick_params(rotation=...) | 45° for dense dates |
Numeric axes: control spacing and labels
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator, FuncFormatter
import numpy as np
x = np.linspace(0, 50, 200)
y = np.log1p(x) * 3.2
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot(x, y, color="tab:blue")
# Place ticks every 10 units on x, every 1.5 on y
ax.xaxis.set_major_locator(MultipleLocator(10))
ax.yaxis.set_major_locator(MultipleLocator(1.5))
# Custom label with units
ax.yaxis.set_major_formatter(FuncFormatter(lambda val, _: f"{val:.1f} dB"))
ax.set_xlabel("Samples")
ax.set_ylabel("Signal (dB)")
ax.grid(True, axis="both", linestyle="--", alpha=0.3)
plt.tight_layout()
plt.show()Tips:
- Use
MaxNLocator(nbins=6)when data ranges vary; it keeps the axis neat across plots. - Pair formatter and locator: formatters do not change tick positions, only text.
- Keep units in labels or formatters, not both, to avoid duplication.
Dates: prevent overlap and keep context
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import pandas as pd
dates = pd.date_range("2024-01-01", periods=120, freq="D")
values = pd.Series(range(len(dates))).rolling(7, min_periods=1).mean()
fig, ax = plt.subplots(figsize=(8, 4))
ax.plot(dates, values, color="tab:green")
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"))
ax.xaxis.set_minor_locator(mdates.WeekdayLocator(byweekday=mdates.MO, interval=2))
plt.setp(ax.get_xticklabels(), rotation=30, ha="right")
ax.set_ylabel("7-day avg")
ax.grid(True, which="both", axis="x", linestyle=":", alpha=0.4)
plt.tight_layout()
plt.show()Tips:
- Use minor locators (e.g., weekly) for subtle grid context without label clutter.
- Rotate date labels 30–45° with
ha="right"to prevent overlap on dense ranges. - For mixed time spans, switch to
AutoDateLocator+ConciseDateFormatterto let Matplotlib adapt.
Dual axes: keep ticks aligned
When using twinx/secondary_yaxis, apply locators and formatters separately to each axis so colors, ticks, and units stay consistent. Align legends as shown in the secondary axis guide.
Quick debugging checklist
- Random tick jumps? Set a deterministic locator (e.g.,
MultipleLocator). - Labels clipped? Enable
constrained_layout=Trueor addplt.tight_layout(). - Too many ticks? Drop to
MaxNLocator(nbins=5)or hide minors withax.tick_params(which="minor", length=0).