Matplotlib·Lesson 13·12 min·0/3 exercises

Subplot grids

Small multiples, and the array shape that breaks your loop

plt.subplotsaxes.flatshareysqueezefig.delaxessmall multiples

Watch it happen

Play it through, or step back and forth yourself.

[0, 0]
[0, 1]
[0, 2]
[1, 0]
[1, 1]
[1, 2]
fig, axes = plt.subplots(
    2, 3,                 # rows, then columns
    figsize=(9, 5),
)

axes.shape        # (2, 3)
axes[0, 2]        # top right
len(fig.axes)     # 6

plt.subplots(2, 3) — rows first, then columns. One Figure, six Axes, and axes comes back as a numpy array you index like any other: axes[0, 2].

The idea

plt.subplots(2, 3) — rows first, then columns. One Figure, six Axes, and axes comes back as a numpy array you index normally:

fig, axes = plt.subplots(2, 3, figsize=(9, 5))

axes.shape        # (2, 3)
axes[0, 2]        # top right
len(fig.axes)     # 6

The shape trap

The shape of axes depends on how you called it, which is a real source of breakage:

plt.subplots()        # a bare Axes — not an array at all
plt.subplots(1, 3)    # shape (3,)   -> axes[1]
plt.subplots(3, 1)    # shape (3,)   -> axes[1]
plt.subplots(2, 3)    # shape (2, 3) -> axes[0, 1]

So a loop written for a 1×3 row raises IndexError the day someone makes it 2×3 — or worse, axes[1] quietly becomes an entire row of Axes rather than one of them. Two ways out:

for ax, name in zip(axes.flat, names):    # works for any shape
    ax.plot(days, week[name])

fig, axes = plt.subplots(1, 3, squeeze=False)   # always 2-D

axes.flat is the one to build the habit around. It iterates every Axes in reading order regardless of the grid, and it's shorter than what it replaces.

Small multiples

This is what grids are for. The same chart repeated once per category, same scale, same style, so the only thing varying between panels is the data:

fig, axes = plt.subplots(2, 3, figsize=(10, 5), sharey=True, layout="constrained")

for ax, item in zip(axes.flat, items):
    sub = orders[orders["item"] == item]
    ax.hist(sub["wait"], bins=12)
    ax.set_title(item, fontsize=10)

Five overlapping histograms on one Axes is a mess; five small ones side by side is readable at a glance. The general rule: when you're tempted to put a fifth series on one chart, make small multiples instead.

Sharing is what makes it work

sharey=True is not a nicety here — it's the thing that makes the comparison valid. Without it every panel autoscales to its own data, so a panel of tiny numbers looks identical to a panel of huge ones and the grid actively misleads.

fig, axes = plt.subplots(2, 3, sharey=True)    # one y scale for all
fig, axes = plt.subplots(3, 1, sharex=True)    # stacked time series

Sharing also hides the interior tick labels, which is why a shared grid looks tidier than one you spaced by hand — no repeated axis running down the middle. And it links the views, so zooming one panel interactively moves the rest.

Leftover panels

Five categories in a 2×3 grid leaves an empty box, and an empty box reads as missing data rather than spare capacity:

for extra in axes.flat[len(items):]:
    fig.delaxes(extra)

Better still, pick a grid that fits — 1×5, or 2×3 with something useful in the sixth cell.

Sizing

figsize is the whole figure, so a 2×3 grid at the default 6.4×4.8 gives each panel about 2×1.6 inches, which is too small for axis labels. Scale it with the grid — roughly 3×2.2 inches per panel is a reasonable starting point.

See it run

The lesson's code, ready to run and to fiddle with.

Putting the kettle on…

Starting up…

Worked example

not graded

Already written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.

tryremoving sharey=True and watching the panels stop being comparable.

Press Run — the output appears here.

Your turn

3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.

Make a 2 by 3 grid of Axes and return [list(axes.shape), len(fig.axes)].

your answer

Make a 2×3 grid and give every panel a title — "0" through "5" in reading order — using axes.flat. Return the six titles as a list.

your answer

Make a 1×2 grid that shares its y axis, plot delhi on the left only, and return whether both panels ended up with the same limits: axes[0].get_ylim() == axes[1].get_ylim().

your answer