Figures, axes and your first plot

The object model behind every Matplotlib plot, and how to avoid the state-machine habits that cause inconsistent charts.

Figure versus Axes

A Figure is the whole image or window; an Axes is one plotting area inside it. Confusing the two is why people fight the library: almost everything you want to control lives on the Axes, not the Figure.

import matplotlib.pyplot as plt

fig, ax = plt.subplots(figsize=(7, 4))     # the explicit (object) style
ax.plot([1, 2, 3, 4], [1, 4, 9, 16], marker="o", label="y = x²")
ax.set_title("Growth")
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.legend()
ax.grid(True, alpha=0.3)
fig.tight_layout()
fig.savefig("plot.png", dpi=150)
💡
Prefer fig, ax = plt.subplots() over the bare plt.plot() state machine. Explicit objects behave predictably in functions and notebooks, where the "current axes" is easy to lose track of.

The four plots that cover most work

ax.plot(x, y)                 # line: trends over a continuous axis
ax.scatter(x, y, s=20, alpha=0.6)   # scatter: relationship between two measures
ax.bar(labels, values)        # bar: comparison across categories
ax.hist(values, bins=30)      # histogram: distribution of one variable

ax.barh(labels, values)       # horizontal bar - better for long labels
ax.boxplot(values)            # spread and outliers per group
ax.imshow(matrix)             # heatmap-style matrix
Question you are answeringChart
How does it change over time?Line
Do these two measures relate?Scatter
How do categories compare?Bar
How is one variable distributed?Histogram or box
How does a matrix of values look?Heatmap (imshow or pcolormesh)
⚠️
Truncated axes exaggerate differences. Matplotlib usually starts bars at zero but lines where the data is — decide deliberately, and never let a bar chart start anywhere but zero.

Making a chart readable

ax.set_xlim(0, 10)
ax.set_ylim(bottom=0)
ax.tick_params(axis="x", rotation=45)
ax.annotate("peak", xy=(4, 16), xytext=(5, 12),
            arrowprops={"arrowstyle": "->"})

ax.legend(loc="upper left", frameon=False)
fig.suptitle("Quarterly revenue")
  • Label both axes with units — an unlabelled axis is an unfinished chart.
  • Prefer direct labels on the lines over a legend the reader has to decode.
  • Turn off chart junk: heavy frames, gradients, 3-D effects.
  • Set dpi and figsize for the destination: 150 dpi for documents, larger for print.

FAQ

Why does my plot not show up?
In a script you must call plt.show(). In a notebook it renders automatically — and that is also why stale figures pile up if you keep using the state machine.
How do I make subplots?
fig, axes = plt.subplots(2, 2) then index axes[0, 1]. Use fig.tight_layout() to stop labels overlapping.

Styling, subplots and saving Series and DataFrame

Last refreshed 2026-09-18.