21 Matplotlib: The Absolute Minimum You Must Know
Matplotlib’s gallery is enormous, but every plot you’ll ever make rests on one mental model — the Figure and the Axes — and one idiom for using it. Learn that and the tutorials, the gallery, and the AI-generated snippets all snap into focus.
21.1 The Mental Model: Figure and Axes
A Figure is the whole canvas — the page you’ll save or display. An Axes is one plot living on that canvas: an x-axis, a y-axis, and the data drawn between them (one Figure can hold several Axes, side by side). The one true idiom asks for both, then does everything through the Axes object:
import matplotlib.pyplot as plt # the universal abbreviation>>> fig, ax = plt.subplots()
>>> _ = ax.plot([2021, 2022, 2023, 2024], [10, 12, 9, 15])Why not plt.plot(...), like half the tutorials on the internet? Those plt.* calls are a convenience layer that operates on the “current” figure — an invisible global. With one plot it works; the moment you have two plots, a loop, or a function that draws charts, “current” stops being what you think it is and titles land on the wrong plot. fig, ax = plt.subplots() gives you explicit handles, so there’s never a doubt about which plot you’re talking to.
(The _ = is a doc-transcript habit: plotting methods return the objects they create, and we’re ignoring them. In a script you just call ax.plot(...) bare.)
21.2 Labels, Title, Legend
An unlabelled plot is a rumour, not evidence (TAMYMN-Visualisation.md is the doc on what to plot and how charts mislead). Every Axes gets three lines minimum:
And because the Axes is a real object, you can ask it questions afterwards — which is also how you test plotting code without ever looking at a screen:
>>> fig, ax = plt.subplots()
>>> _ = ax.plot([2021, 2022, 2023, 2024], [10, 12, 9, 15], label='Widgets')
>>> _ = ax.plot([2021, 2022, 2023, 2024], [8, 11, 13, 14], label='Gadgets')
>>> _ = ax.set_title('Sales by year')
>>> _ = ax.set_xlabel('Year')
>>> _ = ax.set_ylabel('Units sold (thousands)')
>>> legend = ax.legend() # built from the label= of each plotted line
>>> ax.get_title()
'Sales by year'
>>> [t.get_text() for t in legend.get_texts()]
['Widgets', 'Gadgets']21.3 Other Charts, Same Idiom
Every chart type is just a different verb on the same ax: ax.bar(labels, heights) for comparisons, ax.hist(values) for distributions, ax.scatter(x, y) for relationships, ax.plot(x, y) for trends. Choosing among them is a thinking skill, not a matplotlib skill — that’s TAMYMN-Visualisation.md.
Several plots on one canvas is where the explicit-handles idiom pays off:
>>> fig, (left, right) = plt.subplots(1, 2) # one figure, two axes
>>> _ = left.bar(['A', 'B'], [3, 5])
>>> _ = right.scatter([1, 2, 3], [2, 4, 6])
>>> len(fig.axes)
221.4 Saving (and the plt.show() Question)
Save from the figure — it’s the canvas, after all:
>>> fig, ax = plt.subplots()
>>> _ = ax.plot([2021, 2022, 2023, 2024], [10, 12, 9, 15])
>>> import io
>>> buf = io.BytesIO() # in a script: fig.savefig('sales.png', dpi=150)
>>> fig.savefig(buf, format='png')
>>> buf.getvalue()[:8] == b'\x89PNG\r\n\x1a\n' # a real PNG came out
True
>>> plt.close('all') # done with these figures; free themplt.show() opens an interactive window and blocks until you close it — it belongs only at the very end of a desktop script, if anywhere. In Jupyter (TAMYMN-Jupyter.md) figures render automatically without it, and on a server there is no screen at all. If your goal is a file, fig.savefig(...) alone is the whole job — and it must come before any show(), because closing the window discards the figure.
21.5 Directing the Machine
AI assistants were trained on two decades of plt.* tutorials, so a vague prompt gets you the global-state style and a plt.show() you didn’t want. An informed prompt names the model on this page — Figure, Axes, the subplots idiom — and states the output.
Vague:
"make a chart of my sales data in python"
Informed:
"Using fig, ax = plt.subplots(), draw revenue vs month as a line on ax. X label
'Month', y label 'Revenue (AUD)', title stating the takeaway, legend from label=.
Finish with fig.savefig('revenue.png', dpi=150) — no plt.show(), this runs
headless in CI."
21.6 Spot the Confabulation
An AI assistant explains how to display a plot and also keep a copy:
plt.plot(months, revenue)
plt.title('Monthly revenue')
plt.show() # inspect it on screen first
plt.savefig('revenue.png') # then save the same figure for the report
What’s wrong?
The order. In a script, plt.show() hands the figure to the window, and closing that window destroys it — afterwards the “current figure” is a brand-new empty one, so plt.savefig writes a blank white PNG. No exception is raised, which is why this bug ships. Save first, then show — or better, hold explicit handles (fig, ax = plt.subplots()) and call fig.savefig(...), which works regardless of what the global “current figure” happens to be.
21.7 Where to Practice
- The Matplotlib gallery — every thumbnail links to complete runnable source. The practice loop: pick a chart, run its code, then convert it to the
fig, axidiom and swap in your own data. No signup. - The Python Graph Gallery — hundreds of copy-paste matplotlib examples organised by chart type, free and signup-less.
21.8 Quick Reference
| Idiom | What it does |
|---|---|
fig, ax = plt.subplots() |
the one true starting line: canvas + one plot |
fig, (a, b) = plt.subplots(1, 2) |
one canvas, two plots side by side |
ax.plot(x, y, label='...') |
line (trend) |
ax.bar(labels, heights) |
bars (comparison) |
ax.hist(values) |
histogram (distribution) |
ax.scatter(x, y) |
points (relationship) |
ax.set_title/set_xlabel/set_ylabel |
the three lines every plot gets |
ax.legend() |
legend built from each line’s label= |
ax.get_title(), fig.axes |
ask the objects — how you test plots |
fig.savefig('out.png', dpi=150) |
write the file — before any show() |
plt.show() |
interactive window; end-of-script only, never headless |
plt.close('all') |
discard figures you’re done with |
That covers the absolute minimum! You can now build, label, combine, save, and test any plot through its Figure and Axes handles — every fancier chart in the gallery is the same idiom with a different verb on ax.