GridSpec is Matplotlib’s tool for defining a grid of subplot regions and placing Axes into specific cells — including cells that span multiple rows or columns. It’s the way to build complex layouts that the simpler plt.subplots(rows, cols) can’t express.

The idea is two-step: first define the grid, then place Axes into chosen cells.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
 
fig = plt.figure()
gs = gridspec.GridSpec(2, 2, figure=fig, left=0.1)
 
ax1 = fig.add_subplot(gs[0, 0])     # top-left
ax2 = fig.add_subplot(gs[0, 1])     # top-right
ax3 = fig.add_subplot(gs[1, 0])     # bottom-left
ax4 = fig.add_subplot(gs[1, 1])     # bottom-right

The GridSpec(2, 2, ...) call defines a 2-row, 2-column grid. gs[0, 0] refers to the top-left cell; fig.add_subplot(gs[0, 0]) places an Axes there. The left=0.1 argument sets a small left margin.

GridSpec’s real strength is spanning Axes across multiple cells using slice notation:

ax_top   = fig.add_subplot(gs[0, :])    # row 0, all columns — spans both
ax_left  = fig.add_subplot(gs[1, 0])    # row 1, column 0
ax_right = fig.add_subplot(gs[1, 1])    # row 1, column 1

This builds a layout with one wide chart on top and two narrow charts below it. The slicing gs[0, :] says row 0, every column, and the resulting Axes occupies the full width of the top row.

The grid can be finer than 2×2 for more nuanced layouts:

gs = gridspec.GridSpec(4, 4, figure=fig)
ax_main  = fig.add_subplot(gs[1:4, 0:3])     # 3×3 main panel
ax_top   = fig.add_subplot(gs[0,   0:3])     # marginal on top
ax_right = fig.add_subplot(gs[1:4, 3])       # marginal on the right

This is the joint plot with marginal histograms layout common in exploratory data analysis.

For simpler layouts where every cell is one Axes, plt.subplots(rows, cols) is shorter. GridSpec is the right tool when the layout is irregular — Axes of different sizes, or Axes that span unevenly.