Uneven layouts
Draw the arrangement you want and get back Axes named after it
plt.subplot_mosaicfig.add_gridspecwidth_ratiosfig.add_subplotfig.add_axesWatch it happen
Play it through, or step back and forth yourself.
fig, axes = plt.subplots(2, 3) # every cell the same size. # what if the top one should # be the width of all three?
Real figures are lopsided: one wide chart across the top, three small ones beneath. A plain subplots(2, 3) can't express that, because every cell is the same size.
The idea
Real figures are lopsided. One wide trend across the top, three small panels beneath it; a big chart with a strip of context down the side. An even grid can't express that, because every cell is the same size.
subplot_mosaic
The modern answer, and it's a good one — you draw the layout:
fig, axd = plt.subplot_mosaic(
"""
AAA
BCD
""",
figsize=(9, 5),
layout="constrained",
)
axd["A"].plot(when, cups)
axd["B"].barh(items, sold)Repeat a name and that panel spans the cells you repeated it across. Two things follow, and both matter more than they sound:
The source looks like the figure. Anyone reading the code knows the arrangement without running it, which is not true of a page of add_subplot(gs[0, :2]).
You get a dict, keyed by your names. axd["A"], not axes[0, 1]. Insert a panel later and nothing renumbers — every existing reference keeps working.
One character per cell
Here's the catch, and it will bite you within about ten minutes. In the string form, every cell is a single character. matplotlib reads the string character-by-character, so this:
plt.subplot_mosaic("""
trend trend
items wait
""")does not make two named panels. It tries to make an 11-column grid out of the letters of t, r, e, n, d… and raises ValueError: All of the rows must be the same length, which is a very confusing message if you thought you were writing words.
For readable names, pass nested lists — and this is the form worth defaulting to:
fig, axd = plt.subplot_mosaic([
["trend", "trend", "trend"],
["items", "wait", "temp" ],
])
axd["trend"].plot(when, cups)So: single letters for a quick sketch, nested lists for anything you'll keep. The letters are faster to type; the names are what makes the drawing code readable, which was the point of using mosaic in the first place.
Blanks
plt.subplot_mosaic("""
AAB
C.B
""")
plt.subplot_mosaic([["trend", "trend"],
["items", "." ]])A "." leaves the cell empty — room for a figure-level legend, or a deliberately ragged shape. It never appears in the returned dict, and it's visible in the source, which a deleted Axes isn't.
GridSpec
Mosaic is built on GridSpec, and you drop down to it when the cells shouldn't be equal:
fig = plt.figure(figsize=(9, 5), layout="constrained")
gs = fig.add_gridspec(2, 3, width_ratios=[2, 1, 1], height_ratios=[1, 2])
top = fig.add_subplot(gs[0, :]) # the whole top row
left = fig.add_subplot(gs[1, 0])
mid = fig.add_subplot(gs[1, 1])gs slices like an array, so gs[0, :] is the top row and gs[:, 0] the left column. width_ratios=[2, 1, 1] makes the first column twice as wide as the others. GridSpec also nests — gs[1, 0].subgridspec(2, 1) — for the rare figure that needs it.
Mosaic accepts the same ratios, so you often don't need to drop down at all: subplot_mosaic(..., width_ratios=[2, 1, 1]).
Exact placement
inset = fig.add_axes([0.62, 0.62, 0.25, 0.22]) # [left, bottom, w, h]Fractions of the figure, ignoring every grid. This is how you put an inset zoom in a corner or place a colourbar exactly. It's also outside the layout engine's control, so it won't be moved out of the way of anything — use it deliberately, not as a default.
(There's also ax.inset_axes([...]), which positions relative to a parent Axes rather than the figure — usually what you actually want for a zoom.)
Which to reach for
- subplots — an even grid. Still the common case.
- subplot_mosaic — anything lopsided. The right default now.
- GridSpec — explicit ratios or nesting.
- add_axes / inset_axes — insets and colourbars, placed by hand.
All four build the same kind of Axes. They differ only in how you describe where it goes.
See it run
The lesson's code, ready to run and to fiddle with.
Putting the kettle on…
Starting up…
Worked example
not gradedAlready written and ready to go — press Run to see what it does, then change a number, a column name, anything, and run it again.
tryrearranging the lists to put "trend" down the left instead: ["trend", "items"], ["trend", "wait"].
Your turn
3 exercises. Write the code yourself, then press Check — a nudge and the answer are there if you want them.
Build a mosaic with a panel called top spanning two columns, and left and right beneath it. Return [sorted(axd), len(fig.axes)].
Same layout, but leave the bottom-right cell empty using ".". Return [sorted(axd), len(fig.axes)] — there should now be two panels, not three.
Use fig.add_gridspec to make a 2×3 grid whose first column is twice as wide as the others, then add one Axes spanning the whole top row. Return [gs.get_width_ratios(), len(fig.axes)].
