Complex layouts: constrained, mosaic, GridSpec

Matplotlib · Advanced · 14 min

Dashboards and paper figures rarely fit a uniform grid, and manual spacing tweaks break whenever labels change.

Constrained layout (layout="constrained" in plt.subplots or plt.figure) adjusts Axes positions at draw time so titles, labels, colorbars and legends fit without overlapping. It replaces calling fig.tight_layout() after the fact and handles colorbars and figure legends better. Tune spacing with fig.get_layout_engine().set(w_pad=..., h_pad=..., wspace=..., hspace=...).

fig.subplot_mosaic describes a layout with a picture:

axd = fig.subplot_mosaic([["top", "top"],
                          ["left", "right"]])

Repeating a label makes that Axes span those cells. Use "." for an empty cell. The result is a dict keyed by label, which is more readable than array indexes. A string shorthand works too: "AAB;CCB" uses ; between rows. width_ratios and height_ratios set relative sizes.

GridSpec is the lower-level tool: gs = fig.add_gridspec(3, 3) then fig.add_subplot(gs[0, :]) takes the full top row and gs[1:, :2] a 2x2 block. Nested grids come from gs[0].subgridspec(2, 2).

Subfigures (fig.subfigures(1, 2)) split a figure into independent regions, each with its own suptitle, colorbar and layout, which is useful when the left half is a grid of images and the right half a single chart.

Constrained layout is not compatible with manual fig.subplots_adjust; choose one or the other.

Example

import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np

fig = plt.figure(figsize=(8, 6), layout="constrained")
axd = fig.subplot_mosaic(
    [["trend", "trend", "hist"],
     ["map",   "map",   "hist"],
     ["map",   "map",   "."]],
    width_ratios=[1, 1, 0.8],
)
rng = np.random.default_rng(0)
axd["trend"].plot(rng.normal(size=100).cumsum())
axd["hist"].hist(rng.normal(size=500), orientation="horizontal")
im = axd["map"].imshow(rng.random((20, 30)), aspect="auto")
fig.colorbar(im, ax=axd["map"])
for name, ax in axd.items():
    ax.set_title(name)

print(sorted(axd))                     # ['hist', 'map', 'trend']
print(type(fig.get_layout_engine()).__name__)  # ConstrainedLayoutEngine
fig.savefig("mosaic.png")

Remember

  • layout='constrained' prevents overlaps, including with colorbars
  • subplot_mosaic draws a layout from a picture and returns a dict of Axes
  • GridSpec slices and subfigures cover nested and spanning layouts

Watch out for

  • Combining constrained layout with subplots_adjust, which disables or fights the engine.

Check yourself

  1. In `fig.subplot_mosaic([['a', 'a'], ['b', 'c']])`, what is Axes 'a'?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: matplotlib.org/stable/users/explain/axes/mosaic.html

Builds on

Unlocks

Nothing builds on this yet.