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.
Practice
Write it yourself. The answer is there when you want it.
Putting the kettle on…
Starting up…
Write it yourself
not gradedLay out four named panels with subplot_mosaic — a wide trend across the top and three smaller ones beneath it. Use nested lists rather than a string, so the names can be words. Fill them: daily cups resampled from orders, the menu as sorted horizontal bars, the wait times as a histogram, and temperature against cups as a scatter, each titled from the left. Print the panel names and the Axes count. Finish with fig.
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)].
