Subplots & Layouts

Reviewed & published by Brayan K

Matplotlib is a Python library for creating charts and visualizations — and subplots let you arrange several charts in a grid inside one figure so they can be compared at a glance.

Part of the free Matplotlib course at LearnCodingFast — hands-on lessons with worked examples and the output they print, plus practice exercises and a quick quiz.

In this lesson you'll create a grid with plt.subplots(), draw on each cell, share axes, and tidy the spacing with tight_layout.

What You'll Learn in This Lesson

1 Creating a Grid of Plots

plt.subplots(rows, cols) builds a grid in one call and returns the figure and an array of axes. You then draw on each cell with axs[row, col] — a more explicit, object-oriented style than the plain plt. calls.

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 10, 100)

# A 2-by-2 grid of plots, 10x8 inches
fig, axs = plt.subplots(2, 2, figsize=(10, 8))

axs[0, 0].plot(x, np.sin(x))
axs[0, 0].set_title("Sine")

axs[0, 1].plot(x, np.cos(x))
axs[0, 1].set_title("Cosine")

axs[1, 0].plot(x, x)
axs[1, 0].set_title("Linear")

axs[1, 1].plot(x, np.sqrt(x))
axs[1, 1].set_title("Square Root")

plt.tight_layout()
plt.show()
The chart this code draws: Draw four functions in one figure
What this code draws, rendered with Matplotlib.

What you'll see: one figure split into four panels. The top row shows a sine and cosine wave, the bottom row a straight line and a square-root curve, each with its own title, and tight_layout keeps them neatly spaced.

2 Sharing Axes & a Figure Title

When plots share a scale, pass sharex=True or sharey=True so their axes line up and duplicate tick labels disappear. Add one overarching title with fig.suptitle().

import matplotlib.pyplot as plt
import numpy as np

x = np.linspace(0, 5, 50)

# Two stacked plots sharing the same x-axis
fig, axs = plt.subplots(2, 1, figsize=(8, 6), sharex=True)

axs[0].plot(x, np.exp(x), color="crimson")
axs[0].set_ylabel("exp(x)")

axs[1].plot(x, np.log(x + 1), color="navy")
axs[1].set_ylabel("log(x+1)")
axs[1].set_xlabel("x")

fig.suptitle("Growth vs Diminishing Returns")
plt.tight_layout()
plt.show()
The chart this code draws: Stack two plots that share the x-axis
What this code draws, rendered with Matplotlib.

What you'll see: two charts stacked vertically. The top one rises steeply (an exponential) while the bottom one flattens out (a logarithm). They share one x-axis at the bottom, and a single bold title sits above the whole figure.

3 Mixing Chart Types in One Figure

Each axes is independent, so a single figure can mix a line plot, a bar chart, and a scatter plot. A 1×3 row is perfect for a quick dashboard.

import matplotlib.pyplot as plt
import numpy as np

fig, axs = plt.subplots(1, 3, figsize=(12, 4))

# Line plot
axs[0].plot([1, 2, 3, 4], [10, 20, 15, 25])
axs[0].set_title("Line")

# Bar chart
axs[1].bar(["A", "B", "C"], [5, 9, 3], color="orange")
axs[1].set_title("Bar")

# Scatter plot
np.random.seed(0)
axs[2].scatter(np.random.rand(40), np.random.rand(40), color="green")
axs[2].set_title("Scatter")

fig.suptitle("Mini Dashboard")
plt.tight_layout()
plt.show()
The chart this code draws: Combine line, bar, and scatter in one row
What this code draws, rendered with Matplotlib.

What you'll see: three panels side by side in a single wide figure — a line on the left, an orange bar chart in the middle, and a green scatter cloud on the right — all under one shared "Mini Dashboard" title.

🎯 Your Turn: Fill in the Blanks

Replace each ___ to build a 1×2 grid and tidy the spacing.

import matplotlib.pyplot as plt

# Create a figure with 1 row and 2 columns
fig, axs = plt.___(1, 2, figsize=(10, 4))

# Draw on the first cell
axs[0].plot([1, 2, 3], [1, 4, 9])
axs[0].set_title("Squares")

# Draw on the second cell
axs[1].bar(["X", "Y"], [3, 7])
axs[1].set_title("Bars")

# Fix the spacing
plt.___()

# Display the figure
plt.___()

📋 Common Errors & Quick Reference

❌ TypeError: 'AxesSubplot' object is not subscriptable

With a single row or column, axs is 1D — use axs[0], not axs[0, 0].

❌ Titles overlap the plots below

Call plt.tight_layout() after drawing everything to space the panels out.

CallWhat It Does
plt.subplots(2, 2)Make a 2×2 grid
axs[0, 1].plot(...)Draw on one cell
fig.suptitle("...")Title for the whole figure
plt.tight_layout()Auto-space the panels

🏆 Mini Challenge: Two-Panel Report

Build a side-by-side figure with a line plot and a bar chart, each titled, under one super-title.

import matplotlib.pyplot as plt

fig, axs = plt.subplots(1, 2, figsize=(10, 4))

# Left: monthly trend
months = [1, 2, 3, 4, 5]
sales = [100, 130, 120, 160, 180]
axs[0].plot(months, sales, marker="o")
axs[0].set_title("Sales Trend")

# Right: category totals
axs[1].bar(["North", "South", "East"], [50, 70, 40], color="steelblue")
axs[1].set_title("Region Totals")

fig.suptitle("Quarterly Report")
plt.tight_layout()
plt.show()
The chart this code draws: Two-panel report with line and bar charts
What this code draws, rendered with Matplotlib.
import matplotlib.pyplot as plt

fig, (top, bottom) = plt.subplots(2, 1, sharex=True, figsize=(6, 5))

top.plot([1, 2, 3, 4], [10, 12, 9, 15])
top.set_ylabel("visits")

bottom.bar([1, 2, 3, 4], [3, 1, 4, 2])
bottom.set_ylabel("sales")
bottom.set_xlabel("week")

fig.tight_layout()

print("Axes created:", len(fig.axes))
print("They share the x axis:", top.get_shared_x_axes().joined(top, bottom))
print("Top y label:", top.get_ylabel())
print("Bottom y label:", bottom.get_ylabel())
print("Only the bottom has an x label:", top.get_xlabel() == "" and bottom.get_xlabel() == "week")
print("Same x range:", top.get_xlim() == bottom.get_xlim())

# ✅ Expected output:
# Axes created: 2
# They share the x axis: True
# Top y label: visits
# Bottom y label: sales
# Only the bottom has an x label: True
# Same x range: True
The chart this code draws: Prove sharex really shared the axis, and that only the bottom panel kept its x label
What this code draws, rendered with Matplotlib.

❓ Frequently Asked Questions

Lesson complete — you can build multi-panel figures!

You created grids with plt.subplots(), drew on each cell, shared axes, added a figure title, and used tight_layout to keep everything spaced cleanly.

Practice quiz

What does plt.subplots(2, 2) return?

  • The figure and an array of axes
  • Only a single axes
  • A list of figures
  • The pixel dimensions of the grid

Answer: The figure and an array of axes. plt.subplots(rows, cols) returns the figure and an array of axes.

How do you draw on the top-right cell of a 2x2 grid?

  • axs.plot(0, 1)
  • axs[0, 1].plot(...)
  • axs(0)(1).plot(...)
  • plt.cell(0, 1).plot(...)

Answer: axs[0, 1].plot(...). For a 2D grid, index the axes array like axs[0, 1] for the top-right cell.

Which argument makes subplots share the same x-axis scale?

  • linkx=True
  • samex=True
  • sharex=True
  • joinx=True

Answer: sharex=True. Passing sharex=True aligns the x-axes and removes duplicate inner tick labels.

How do you add one title above all the subplots?

  • ax.set_title('...')
  • plt.title('...')
  • axs.title('...')
  • fig.suptitle('...')

Answer: fig.suptitle('...'). fig.suptitle() places a single super-title at the top of the whole figure.

What sets the overall figure dimensions in plt.subplots()?

  • size=(10, 8)
  • figsize=(10, 8)
  • dimensions=(10, 8)
  • shape=(10, 8)

Answer: figsize=(10, 8). figsize=(width, height) sets the figure size in inches.

Which call auto-spaces panels so titles stop overlapping?

  • plt.fix()
  • plt.spacing()
  • plt.tight_layout()
  • plt.adjust()

Answer: plt.tight_layout(). plt.tight_layout() adjusts spacing after you've drawn everything.

For plt.subplots(1, 3), how is the axes array shaped?

  • A scalar axes
  • A 2D array indexed by [row, col]
  • A dictionary keyed by name
  • A 1D array indexed by axs[0], axs[1], axs[2]

Answer: A 1D array indexed by axs[0], axs[1], axs[2]. A single row gives a 1D array, so you index it with axs[0], axs[1], axs[2].

Can a single figure mix a line, a bar, and a scatter plot?

  • Yes — each axes is independent
  • No, all subplots must be the same type
  • Only with separate figures
  • Only if sharex=True

Answer: Yes — each axes is independent. Each axes is independent, so one figure can mix different chart types.

What error does axs[0, 0] raise on a single-row subplot result?

  • KeyError: 0
  • TypeError: 'AxesSubplot' object is not subscriptable
  • ValueError: bad index
  • IndexError: out of range

Answer: TypeError: 'AxesSubplot' object is not subscriptable. With one row, axs is 1D — use axs[0], not axs[0, 0].

What does the 'rows' value in plt.subplots(rows, cols) control?

  • The figure title
  • The colormap
  • The number of horizontal rows of plots
  • The line color

Answer: The number of horizontal rows of plots. The first argument is the number of rows in the subplot grid.

Continue this course

Frequently asked questions

What does plt.subplots() return?

It returns two things: the figure object and an array (or single object) of axes. The usual call is fig, axs = plt.subplots(rows, cols), after which you draw on each axes individually with axs[row, col].plot(...).

How do I stop subplots from overlapping?

Call plt.tight_layout() after you have drawn everything. It automatically adjusts the spacing so titles and axis labels no longer collide with neighboring plots.

What do sharex and sharey do?

Passing sharex=True or sharey=True makes the subplots share the same axis scale and tick labels. This keeps plots directly comparable and removes duplicate tick labels from the inner plots.

How do I add one title above all the subplots?

Use fig.suptitle('My Title'). It places a single super-title at the top of the whole figure, separate from each subplot's own title set with ax.set_title().