5.4.15 · D5Scientific Computing (Python)

Question bank — Matplotlib — figure - axes architecture

1,736 words8 min readBack to topic

True or false — justify

True or false: "Axes" in Matplotlib means the two axis lines (the x-line and the y-line).
False. "Axes" (plural-looking) is one whole plot region with its own coordinate system; the individual rulers are each an "Axis" (singular). The naming is historically backwards from what it sounds like.
True or false: one Figure can contain many Axes, but one Axes belongs to exactly one Figure.
True. The Figure holds a list of Axes, and each Axes is registered in exactly one parent Figure — that's why fig.savefig captures all of that Figure's Axes in one image.
True or false: plt.plot(x, y) and ax.plot(x, y) add fundamentally different kinds of objects to the plot.
False. Both add the same Line2D Artist. plt.plot just calls gca().plot(...) — the same method on whatever Axes is "current". Only who you address differs, not the result.
True or false: calling plt.title("T") sets a title for the whole Figure.
False. plt.title sets the title of the current Axes only. The Figure-wide title spanning all panels is fig.suptitle("T") — a different level of the hierarchy.
True or false: plt.subplots(1, 1) returns an array of Axes you must index into.
False. With a single cell it returns one bare Axes object (not an array). You'd write ax.plot(...), not ax[0].plot(...). Arrays appear only when a row or column count exceeds one.
True or false: if you never call plt.subplots or plt.figure, then plt.plot(x, y) will error because no Figure exists.
False. plt.plot calls gca(), which creates a Figure and Axes on demand if none is current. That auto-creation is exactly the hidden magic the OO style removes.
True or false: fig.tight_layout() changes the data in your plots.
False. It only rearranges spacing — it recomputes Axes positions inside the Figure so labels and titles stop overlapping. The Line2D data is untouched; only the containers move.
True or false: legends are a Figure-level feature that automatically collects every line you ever drew.
False (by default). ax.legend() reads the labelled Artists of that one Axes. A different Axes has its own separate legend. (There is a fig.legend(), but the everyday call is per-Axes.)

Spot the error

fig, ax = plt.subplots()
plt.plot(x, y)
fig.savefig("out.png")
Why can this silently save a blank-looking result?
plt.plot draws on the current Axes, which here is ax — so it usually works, but the mixing is fragile: the moment another Figure becomes current between these lines, the line lands elsewhere. Rule: in OO style plot with ax.plot(x, y) so the target is never ambiguous.
fig, axes = plt.subplots(2, 2)
axes.plot([0,1], [0,1])
What breaks and why?
axes is a 2×2 NumPy array of Axes, not a single Axes, so it has no .plot method — you get an AttributeError. You must index a specific cell: axes[0, 0].plot(...).
fig, ax = plt.subplots(1, 3)
ax[0][1].plot(x, y)
Why is the indexing wrong here?
With one row and three columns, plt.subplots returns a 1-D array, so you index once: ax[1]. The double index ax[0][1] treats it as 2-D and will fail. 2-D indexing only applies when both nrows and ncols exceed 1.
ax.xlabel("time")
What's the mistake?
xlabel is a pyplot function name; the Axes method is set_xlabel. On an Axes object you write ax.set_xlabel("time"). Mixing the two naming conventions is the single most common OO typo.
fig1, ax1 = plt.subplots()
fig2, ax2 = plt.subplots()
ax1.plot(x, y)
plt.title("My plot")
Which Figure gets the title, and is that what the author wanted?
plt.title targets the current Axes, which is ax2 (the last one created) — so the title lands on the empty second Figure, not on ax1 where the data is. The author almost certainly wanted ax1.set_title("My plot").
fig, ax = plt.subplots()
ax.plot(x, y)
Nothing appears on screen. Where's the error?
There is no rendering trigger. In a script you need plt.show() (interactive window) or fig.savefig(...) (file). The plot exists as objects in memory — you just never asked Matplotlib to display or save it.
fig, ax = plt.subplots()
ax.plot(x, y, label="data")
# (no ax.legend() call)
Why does the label not show?
A label= only stores text on the Line2D Artist; it doesn't draw anything by itself. You must call ax.legend() for the Axes to collect labelled Artists and render the legend box.

Why questions

Why is the OO interface (fig, ax = ...) preferred over pyplot for multi-panel figures?
Because with several Axes there is no sensible "current" one — pyplot's hidden pointer becomes a guessing game. OO addresses each panel explicitly (axes[r, c]), so labels and lines can never land on the wrong subplot.
Why does saving happen at the Figure level (fig.savefig) and not the Axes level?
One saved image = one whole canvas, which is the Figure (possibly holding many Axes). An Axes is only a sub-region; there's no such thing as "saving one panel to a file" through the normal API — the export unit is the Figure.
Why is plt.plot described as a "thin wrapper"?
Because it does almost no work of its own: it calls gca() to find (or create) the current Axes, then calls that Axes' .plot method. All the real drawing logic lives in the Axes; pyplot just picks the target for you.
Why do label-setting methods differ between the two APIs (plt.xlabel vs ax.set_xlabel)?
Pyplot functions are module-level shortcuts named for the action (xlabel), while Axes methods follow the object convention of set_/get_ accessors (set_xlabel). They ultimately touch the same Axis Artist, but the naming reflects what object you're speaking to.
Why can calling plt.plot twice put both curves on one plot when you wanted two?
Pyplot keeps adding Artists to the same current Axes until you deliberately make a new Figure/Axes. Two plt.plot calls with no new Figure in between just stack two Line2D Artists in one box. Fix: plt.subplots() again, or a grid.
Why does confusing "Axes" and "Axis" cause real bugs, not just vocabulary slips?
Because they're objects at different levels: ax (Axes) owns .plot, .set_title, .legend; ax.xaxis (Axis) owns tick locators and formatters. Reaching for a method on the wrong one gives AttributeError, so the words map to which methods are legal.
Why does fig.suptitle exist separately from ax.set_title?
They label different containers. ax.set_title names one panel; fig.suptitle names the whole Figure, spanning all panels — useful when several subplots share one theme. Different scope, different level.

Edge cases

What does plt.subplots(1, 1) return, and how does that differ from plt.subplots(1, 2)?
(1, 1) returns a Figure and a single Axes object. (1, 2) returns a Figure and a 1-D array of two Axes. The array only appears once a dimension exceeds 1 — a frequent surprise.
You call plt.subplots(3, 2). What is the exact type and shape of the second return value?
A 2-D NumPy array of Axes with shape (3, 2); you index it as axes[row, col], e.g. axes[2, 1] for the bottom-right panel. Rows first, columns second.
What happens to the "current Axes" pointer right after you create three Figures in a row?
The last Figure (and its Axes) created becomes current. So a bare plt.plot afterward draws on that third Figure — a classic reason earlier Figures end up mysteriously empty.
If you create a Figure with plt.figure() but never add any Axes, what does plt.plot(x, y) do?
gca() sees a Figure with no Axes and creates a default full-frame Axes inside it, then plots there. So a plot still appears — Matplotlib fills the gap rather than erroring.
Can two different Axes in the same Figure have completely independent x and y limits and scales?
Yes. Each Axes owns its own pair of Axis objects, so limits, tick spacing, and even log-vs-linear scale are per-Axes. They coexist on one Figure without affecting each other unless you explicitly share them (sharex=).
What visibly happens if you plot data far outside an Axes' current x/y limits and never adjust them?
The Line2D Artist is still created and belongs to the Axes, but it's drawn outside the visible window, so the panel looks empty. Calling ax.autoscale() or ax.relim()+ax.autoscale_view() brings the limits back around the data.
What is the smallest valid Figure — can a Figure legally contain zero Axes?
Yes, a freshly made plt.figure() has no Axes at all; it's a blank canvas. It's perfectly valid, just empty — Axes are added on demand (by add_subplot, subplots, or the auto-creation inside gca()).

Recall One-line summary of every trap

Almost every item above reduces to container confusion: an Artist put in, or a method called on, the wrong level of Figure ⊃ Axes ⊃ Artist (with Axis as the per-direction manager). Name the box, address the box.


Connections

  • Matplotlib — pyplot vs object-oriented API
  • Matplotlib — subplots and GridSpec layout
  • Matplotlib — Artists, Line2D and styling
  • NumPy — arrays as plot data
  • Saving figures — dpi, formats, bbox
  • Scientific Computing (Python)