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 colorbarssubplot_mosaicdraws 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.