7.3 Labels, Scales, Annotations, and Uncertainty
A plot can be mathematically correct and still fail as evidence if readers cannot identify the population, units, series, or visual scale. A useful chart should survive separation from the notebook cell that created it.
Give the chart a reading contract
A complete chart usually answers five questions:
1. What comparison or finding is shown?
2. What does each axis measure, and in which units?
3. Which mark belongs to which series or group?
4. What data, filters, and time period are included?
5. Are values observed, estimated, normalized, or uncertain?
Use a specific title rather than a generic label such as “Results”:
from matplotlib.ticker import StrMethodFormatter
fig, ax = plt.subplots(figsize=(8, 5), layout="constrained")
ax.plot(monthly["month"], monthly["north"], marker="o", label="North")
ax.plot(
monthly["month"], monthly["south"],
marker="s", linestyle="--", label="South",
)
ax.set(
title="Monthly completed sales by region, Jan–Jun 2026",
xlabel="Month",
ylabel="Sales (USD)",
)
ax.yaxis.set_major_formatter(StrMethodFormatter("${x:,.0f}"))
ax.legend(title="Region", frameon=False)
fig.text(
0.01,
0.01,
"Source: order warehouse; completed orders only; refreshed 2026-07-02",
fontsize=8,
)A legend is useful when several Artists repeat across a plot, but direct labels near line endpoints can reduce eye travel. Do not distinguish series by color alone. Combine color with line style, marker shape, direct text, or small multiples so the chart remains readable for color-vision differences, grayscale printing, and poor projection conditions.
Ticks should help decode position rather than create decoration. Use locators and formatters instead of manually replacing tick-label text without matching tick positions. Grid lines can aid comparison, especially on a numeric axis, but keep them visually behind the data.
Make scale transformations explicit
On a linear axis, equal visual distances represent equal additive differences. On a base-10 logarithmic axis, equal distances represent equal ratios:
Set the scale on the Axes and say so in the title, label, caption, or tick structure:
assert metrics["latency_ms"].gt(0).all()
fig, ax = plt.subplots(layout="constrained")
ax.plot(metrics["week"], metrics["latency_ms"], marker="o")
ax.set_yscale("log")
ax.set(
title="Weekly latency on a logarithmic scale",
xlabel="Week",
ylabel="Latency (ms, log scale)",
)A standard log axis cannot display zero or negative values. Do not silently add a constant merely to make them plottable. Investigate whether the values are valid, filter with a documented population change, retain a linear scale, or use a scale such as symlog when its behavior matches the analytical need.
Log scales are helpful across orders of magnitude, but they can make absolute differences look smaller. A linear and log view side by side can reveal which conclusion depends on scale.
Annotate evidence without inventing causality
ax.annotate connects text to a data location. xy identifies the target, while xytext and textcoords control the label position:
ax.annotate(
"Release deployed",
xy=(release_date, release_latency),
xytext=(14, 28),
textcoords="offset points",
arrowprops={"arrowstyle": "->"},
)The annotation records known event timing. It does not prove that the release caused the following change. Avoid arrows and wording that turn coincidence into a causal story.
Uncertainty is also evidence. errorbar accepts symmetric errors or separate lower and upper magnitudes:
import numpy as np
mean = summary["mean"].to_numpy()
low = summary["ci_low"].to_numpy()
high = summary["ci_high"].to_numpy()
yerr = np.vstack([mean - low, high - mean])
assert np.all(yerr >= 0)
ax.errorbar(
summary["week"],
mean,
yerr=yerr,
fmt="o-",
capsize=4,
)The chart or caption must state whether bars represent a standard deviation, standard error, prediction interval, or confidence interval; include the confidence level, sample size, and method when relevant. Error bars describe uncertainty under assumptions. They are not a universal threshold for whether groups “differ.”
Labels and annotations compete for limited space. Section 7.4 organizes that space across several panels and separates reusable drawing logic from local style and export decisions.