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 answering | Chart |
|---|---|
| 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
dpiandfigsizefor 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.Related
Styling, subplots and saving Series and DataFrame
Last refreshed 2026-09-18.