Scales, ticks and date axes
Log and symmetric-log scales, tick locators and formatters, and getting date and category axes to read the way you intend.
Scales and limits
ax.set_yscale("log") # orders of magnitude
ax.set_xscale("symlog", linthresh=1.0) # log scale that tolerates zero
ax.set_xscale("logit") # proportions between 0 and 1
ax.set_xlim(0, 100)
ax.set_ylim(bottom=0) # keep the top automatic
ax.invert_yaxis() # depth, rank and lat/lon charts
ax.margins(x=0.02, y=0.05) # breathing room around the data
ax.autoscale(enable=True, axis="y", tight=False)- A log scale cannot represent zero or negatives; those points are masked out, which looks like missing data.
- Use a log scale when ratios matter and a linear scale when differences matter.
symlogkeeps a linear region near zero so you can show zeros alongside large values.- Always state the scale in the axis label — readers assume linear.
Tick locators and formatters
from matplotlib.ticker import (MultipleLocator, MaxNLocator,
PercentFormatter, FuncFormatter)
ax.xaxis.set_major_locator(MultipleLocator(10))
ax.yaxis.set_major_locator(MaxNLocator(nbins=6))
ax.yaxis.set_major_formatter(PercentFormatter(decimals=0))
ax.yaxis.set_major_formatter(FuncFormatter(lambda v, pos: f"{v/1000:.0f}k"))
ax.tick_params(axis="x", rotation=45, labelsize=9, length=3)
ax.minorticks_on()
ax.grid(True, which="major", alpha=0.25)| Locator | Places ticks at | Typical use |
|---|---|---|
MultipleLocator(10) | Fixed intervals | Predictable grids |
MaxNLocator(nbins=6) | Up to n nice values | Automatic but controlled |
FixedLocator([...]) | Exact positions | Hand-picked categories |
LogLocator | Powers of the base | Log axes |
AutoMinorLocator(2) | Subdivisions | Denser grid without labels |
Date and category axes
import matplotlib.dates as mdates
ax.plot(dates, values)
ax.xaxis.set_major_locator(mdates.MonthLocator(interval=1))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%b %Y"))
fig.autofmt_xdate() # rotate and align the labels
# zooming into a range: pick the units that fit the span
ax.xaxis.set_major_locator(mdates.DayLocator(interval=7))
ax.xaxis.set_major_formatter(mdates.DateFormatter("%d %b"))
# category axis: keep the labels you supplied
ax.bar(labels, values)
ax.set_xticks(range(len(labels)), labels, rotation=30, ha="right")⚠️
Pass real datetimes, not strings. A string axis is treated as categories, which spaces irregular dates evenly and silently misleads about the timing of events.
FAQ
Why have my zero values disappeared on a log axis?
They cannot be plotted. Use
symlog with a suitable linthresh, or plot the values with an offset and label the axis accordingly.How do I get thousands separators on the axis?
Use a formatter:
ax.yaxis.set_major_formatter("{x:,.0f}".format) or a FuncFormatter for anything more elaborate.Related
Debugging plots: empty axes, missing data and overlap Chart types: bar, scatter, histogram, pie and box
Last refreshed 2026-09-18.