Skip to content

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

TaskToolExample use
Set tick spacing by stepMultipleLocatorEvery 5 units on x
Limit to a max countMaxNLocatorKeep ≤ 6 ticks on y
Date ticksAutoDateLocatorAuto months/years
Custom labelsFuncFormatterAppend units, add suffix
Rotate labelstick_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 + ConciseDateFormatter to 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=True or add plt.tight_layout().
  • Too many ticks? Drop to MaxNLocator(nbins=5) or hide minors with ax.tick_params(which="minor", length=0).