Subplots in Matplotlib — Multiple Charts, One Figure
Ever wanted to show a line graph and a bar chart side by side without jumping between separate figures? That's exactly what subplots are for. With plt.subplots(), you can pack multiple charts into one neat figure — perfect for comparisons, dashboards, and looking like you really know what you're doing.
Why Use Subplots?
When you have multiple datasets or chart types that tell related stories, cramming them all into a single graph gets messy fast. Subplots let you split the figure into a grid of individual panels — each with its own data, type, and style — while keeping everything visually connected in one place.
Think of it like a newspaper front page — multiple stories, one layout, zero chaos.
What You'll Learn
- Create a basic 1-row, 2-column subplot layout
- Understand the
figandaxobjects returned byplt.subplots() - Plot different chart types in the same figure
- Share axes between subplots for clean comparisons
- Build a 2×2 grid with four different charts at once
- Adjust spacing so nothing overlaps or gets cut off
Example 1: Basic Side-by-Side Subplots
The simplest case — two charts sitting next to each other in one row:
import matplotlib.pyplot as plt
months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun']
sales = [120, 145, 160, 130, 175, 190]
profit = [30, 40, 45, 35, 55, 60]
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 5))
# Left chart — line graph
ax1.plot(months, sales, color='#e86c2f', marker='o', linewidth=2)
ax1.set_title('Monthly Sales')
ax1.set_xlabel('Month')
ax1.set_ylabel('Units Sold')
ax1.grid(True, linestyle='--', alpha=0.5)
# Right chart — bar graph
ax2.bar(months, profit, color='#3d5afe', width=0.5)
ax2.set_title('Monthly Profit')
ax2.set_xlabel('Month')
ax2.set_ylabel('Profit (₹ thousands)')
ax2.grid(True, linestyle='--', alpha=0.5)
plt.suptitle('Sales & Profit Overview — 2025', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
Understanding the Code
plt.subplots(1, 2): Creates a figure with 1 row and 2 columns — so two charts side by side. Returns afig(the whole canvas) and a tuple of axes(ax1, ax2).figsize=(12, 5): Sets the total width and height of the entire figure — both charts share this space.ax1.plot()andax2.bar(): Each axis gets its own chart type. No interference between them.ax.set_title(),ax.set_xlabel(): Labels go on individual axes, not on the whole figure.plt.suptitle(): Adds a shared title above both charts — the "headline" of the whole figure.plt.tight_layout(): Automatically adjusts padding so labels don't overlap or get clipped.
Example 2: Sharing the X-Axis
When both charts track the same time period, sharing the x-axis makes comparisons cleaner and removes redundant labels:
import matplotlib.pyplot as plt
days = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
steps = [4200, 8100, 6500, 9300, 7800, 11200, 5400]
calories = [1800, 2100, 1950, 2300, 2050, 2600, 1700]
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(10, 7), sharex=True)
ax1.plot(days, steps, color='#e86c2f', marker='o', linewidth=2)
ax1.set_ylabel('Steps')
ax1.set_title('Daily Steps')
ax1.grid(True, linestyle='--', alpha=0.5)
ax2.bar(days, calories, color='#00b894', width=0.5)
ax2.set_ylabel('Calories')
ax2.set_title('Calories Burned')
ax2.grid(True, linestyle='--', alpha=0.5)
plt.suptitle('Weekly Fitness Summary', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
Example 3: A 2×2 Grid of Charts
Four charts, one figure — the classic dashboard layout. This is where subplots really shine:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 10, 100)
fig, axes = plt.subplots(2, 2, figsize=(12, 8))
# Top-left — sine wave
axes[0, 0].plot(x, np.sin(x), color='#e86c2f')
axes[0, 0].set_title('Sine Wave')
axes[0, 0].grid(True, linestyle='--', alpha=0.4)
# Top-right — cosine wave
axes[0, 1].plot(x, np.cos(x), color='#3d5afe')
axes[0, 1].set_title('Cosine Wave')
axes[0, 1].grid(True, linestyle='--', alpha=0.4)
# Bottom-left — scatter plot
axes[1, 0].scatter(x, np.random.rand(100), color='#00b894', s=15, alpha=0.7)
axes[1, 0].set_title('Random Scatter')
axes[1, 0].grid(True, linestyle='--', alpha=0.4)
# Bottom-right — bar chart
categories = ['A', 'B', 'C', 'D', 'E']
values = [23, 45, 31, 60, 42]
axes[1, 1].bar(categories, values, color='#e86c2f', width=0.5)
axes[1, 1].set_title('Category Values')
axes[1, 1].grid(True, linestyle='--', alpha=0.4)
plt.suptitle('Chart Dashboard — 4 Panels', fontsize=14, fontweight='bold')
plt.tight_layout()
plt.show()
With a 2×2 grid, axes is a 2D array — so you access each panel with axes[row, col]. Top-left is axes[0, 0], bottom-right is axes[1, 1]. Easy to remember once you think of it like a table.
Quick Reference
plt.subplots(1, 2)— 1 row, 2 columns (side by side)plt.subplots(2, 1)— 2 rows, 1 column (stacked)plt.subplots(2, 2)— 2×2 grid (4 charts)sharex=True— share the x-axis across all panels in a columnsharey=True— share the y-axis across all panels in a rowplt.suptitle()— add a title above the entire figureplt.tight_layout()— fix spacing so nothing overlapsfig.set_size_inches(w, h)— resize the figure after creation
Mini Project: Build a Personal Dashboard
Time to put it all together. Create a 2×2 subplot figure that tracks four aspects of your week:
- Top-left: Daily step count as a line graph
- Top-right: Hours of sleep as a bar chart
- Bottom-left: Screen time per day as a horizontal bar
- Bottom-right: Mood score (1–10) plotted as a scatter chart
Use real data from your phone's health or screen time app if you can — it hits different when the numbers are actually yours.
plt.suptitle("My Week at a Glance") as the headline and use consistent colors across all four panels so the dashboard feels cohesive.
Line Graphs · Bar Graphs · Pie Charts · Scatter Plots · Histogram · Multiple Line Graphs