Matplotlib Grid
Learn how to create multi-plot grid layouts and add background gridlines in Matplotlib using subplots, GridSpec, subplot_mosaic, and grid styling options.
Matplotlib supports two distinct uses of the word "grid": multi-plot grid layouts (arranging several axes side by side in a figure) and background gridlines (the reference lines drawn behind the data on a single axis). This chapter covers both, starting with layout grids and ending with gridline styling.
You should already be comfortable with basic plotting — if not, see the Matplotlib Plotting and Matplotlib Subplot chapters first.
Multi-Plot Grid Layouts
When you need to display several related plots in one figure, you arrange them in a grid of rows and columns. Matplotlib offers three approaches, each suited to different levels of complexity.
plt.subplots() — the quick way
plt.subplots(nrows, ncols) is the fastest route for uniform grids where every subplot is the same size. It returns a Figure and a NumPy array of Axes.
import matplotlib.pyplot as plt
# 2-row, 2-column grid — four equal subplots
fig, axs = plt.subplots(nrows=2, ncols=2, figsize=(8, 6))
axs[0, 0].plot([1, 2, 3], [4, 5, 6])
axs[0, 0].set_title("Line plot")
axs[0, 1].scatter([1, 2, 3], [4, 5, 6])
axs[0, 1].set_title("Scatter plot")
axs[1, 0].bar([1, 2, 3], [4, 5, 6])
axs[1, 0].set_title("Bar chart")
axs[1, 1].hist([1, 2, 3, 4, 5, 6], bins=3)
axs[1, 1].set_title("Histogram")
plt.tight_layout()
plt.show()tight_layout() automatically adjusts spacing so titles and tick labels do not overlap. Pass figsize=(width, height) in inches to control the overall figure size.
Sharing axes
When plots share the same scale you can link their axes so that zooming one zooms all:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(9, 3), sharey=True)
ax1.plot(x, np.sin(x))
ax1.set_title("sin(x)")
ax2.plot(x, np.cos(x))
ax2.set_title("cos(x)")
plt.tight_layout()
plt.show()sharey=True links the y-axis range across both subplots; sharex=True does the same for the x-axis.
GridSpec — variable-size cells
GridSpec lets you assign different weights to rows and columns so that some subplots can be taller or wider than others. It also lets a single subplot span multiple cells.
import matplotlib.pyplot as plt
from matplotlib.gridspec import GridSpec
fig = plt.figure(figsize=(9, 6))
# 2 rows, 3 columns; bottom row is twice as tall
gs = GridSpec(nrows=2, ncols=3, height_ratios=[1, 2], hspace=0.4, wspace=0.3)
ax_top_left = fig.add_subplot(gs[0, 0])
ax_top_mid = fig.add_subplot(gs[0, 1])
ax_top_right = fig.add_subplot(gs[0, 2])
ax_bottom = fig.add_subplot(gs[1, :]) # spans all three columns
ax_top_left.plot([0, 1, 2], [0, 1, 0])
ax_top_left.set_title("Top left")
ax_top_mid.bar([0, 1, 2], [3, 1, 4])
ax_top_mid.set_title("Top centre")
ax_top_right.scatter([0, 1, 2], [2, 7, 1])
ax_top_right.set_title("Top right")
ax_bottom.plot([0, 1, 2, 3, 4], [0, 1, 4, 9, 16], color="steelblue", linewidth=2)
ax_bottom.set_title("Bottom — full width")
plt.show()Key GridSpec arguments:
| Argument | What it does |
|---|---|
width_ratios=[2, 1, 1] | Column widths as relative proportions |
height_ratios=[1, 2] | Row heights as relative proportions |
hspace=0.4 | Vertical gap between rows (fraction of axis height) |
wspace=0.3 | Horizontal gap between columns (fraction of axis width) |
Slicing with gs[row, col] works just like NumPy: gs[1, :] spans all columns; gs[:, 0] spans all rows in the first column.
subplot_mosaic() — layout from a string
Introduced in Matplotlib 3.3, subplot_mosaic() lets you describe the layout with a string or nested list that looks like an ASCII map of the figure. This is often the most readable approach for asymmetric layouts.
import matplotlib.pyplot as plt
layout = """
AB
CC
"""
fig, axes = plt.subplot_mosaic(layout, figsize=(8, 5))
axes["A"].set_title("Panel A")
axes["A"].plot([1, 2, 3], [3, 1, 2])
axes["B"].set_title("Panel B")
axes["B"].scatter([1, 2, 3], [2, 3, 1])
axes["C"].set_title("Panel C — full width")
axes["C"].bar(["x", "y", "z"], [5, 3, 7])
plt.tight_layout()
plt.show()Each unique letter in the string becomes one Axes. Repeated letters in the same row/column cause that subplot to span those cells. The returned axes dict is keyed by label, so accessing panels by name is clearer than numeric indexing.
Controlling spacing: tight_layout vs constrained_layout
Two built-in systems handle automatic spacing:
plt.tight_layout()— call it beforeplt.show(). Adjusts subplot parameters so labels/titles fit. Simple and reliable for most layouts.constrained_layout=True— pass it toplt.subplots()orplt.figure(). Solves a constraint system at draw time; handles colorbars and suptitles better thantight_layout.
import matplotlib.pyplot as plt
# constrained_layout keeps the super-title from overlapping the subplots
fig, axs = plt.subplots(1, 2, figsize=(8, 3), constrained_layout=True)
fig.suptitle("Two plots with constrained_layout", fontsize=13)
axs[0].plot([1, 2, 3], [1, 4, 9])
axs[0].set_title("Subplot 1")
axs[1].plot([1, 2, 3], [9, 4, 1])
axs[1].set_title("Subplot 2")
plt.show()Do not use both at the same time — they conflict. constrained_layout is the more modern choice.
For manual control, fig.subplots_adjust(left, right, top, bottom, hspace, wspace) overrides automatic spacing entirely.
Background Gridlines
On a single axes, ax.grid() draws reference lines at the tick positions. These are different from the layout grid described above — they are visual guides drawn behind the plotted data.
Enabling gridlines
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot([1, 2, 3, 4, 5], [2, 5, 3, 8, 6], marker="o")
# Enable gridlines on both axes
ax.grid(True)
ax.set_title("Plot with gridlines")
plt.tight_layout()
plt.show()Styling gridlines
Pass keyword arguments to ax.grid() to change appearance:
import matplotlib.pyplot as plt
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot([1, 2, 3, 4, 5], [2, 5, 3, 8, 6], marker="o", color="steelblue")
ax.grid(
True,
which="major", # "major", "minor", or "both"
axis="both", # "x", "y", or "both"
color="gray",
linestyle="--",
linewidth=0.7,
alpha=0.7,
)
ax.set_title("Styled gridlines")
plt.tight_layout()
plt.show()Major and minor gridlines
Matplotlib distinguishes major ticks (the labelled ones) from minor ticks (unlabelled subdivisions). You can show gridlines at both levels:
import matplotlib.pyplot as plt
from matplotlib.ticker import MultipleLocator
fig, ax = plt.subplots(figsize=(7, 4))
ax.plot([0, 1, 2, 3, 4, 5, 6], [0, 1, 4, 9, 16, 25, 36])
# Major gridlines every 1 unit, minor every 0.5
ax.xaxis.set_major_locator(MultipleLocator(1))
ax.xaxis.set_minor_locator(MultipleLocator(0.5))
ax.yaxis.set_major_locator(MultipleLocator(10))
ax.yaxis.set_minor_locator(MultipleLocator(5))
ax.grid(True, which="major", linestyle="-", linewidth=0.8, color="gray", alpha=0.6)
ax.grid(True, which="minor", linestyle=":", linewidth=0.5, color="silver", alpha=0.5)
ax.set_title("Major and minor gridlines")
plt.tight_layout()
plt.show()Enabling minor ticks without the locator
If you just want minor ticks shown without placing them manually:
ax.minorticks_on()
ax.grid(True, which="both")Styling with Matplotlib style sheets
Gridlines are also controlled by style sheets. The seaborn-v0_8-whitegrid style, for example, enables horizontal gridlines automatically:
import matplotlib.pyplot as plt
plt.style.use("seaborn-v0_8-whitegrid")
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot([1, 2, 3, 4], [3, 7, 2, 9], marker="s")
ax.set_title("seaborn-v0_8-whitegrid style")
plt.tight_layout()
plt.show()Run plt.style.available to list all built-in styles.
Choosing the Right Approach
| Goal | Best tool |
|---|---|
| Equal-size subplot grid | plt.subplots(nrows, ncols) |
| Subplots with different sizes or spanning cells | GridSpec |
| Asymmetric layout described visually | subplot_mosaic() |
| Reference lines behind data | ax.grid() |
| Fine-grained tick placement for gridlines | MultipleLocator + ax.grid(which=...) |
Related Chapters
- Matplotlib Subplot — deeper dive into
plt.subplots()and axes objects - Matplotlib Labels — adding titles, axis labels, and annotations
- Matplotlib Plotting — line plots, bar charts, and pie charts
- Matplotlib Scatter — scatter plots and marker customisation