matplotlib
data visualization
subplot adjustment
plot spacing
Python plotting

Improve subplot size/spacing with many subplots

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

When creating figures with many subplots in matplotlib, the default spacing often causes overlapping titles, labels, and tick marks. Matplotlib provides several tools to control subplot size and spacing: plt.tight_layout() for automatic adjustment, plt.subplots_adjust() for manual control, GridSpec for advanced layouts, and constrained_layout for a modern automatic approach.

The Problem: Default Spacing

python
1import matplotlib.pyplot as plt
2import numpy as np
3
4fig, axes = plt.subplots(3, 3, figsize=(8, 8))
5for ax in axes.flat:
6    ax.plot(np.random.randn(50))
7    ax.set_title('Title')
8    ax.set_xlabel('X Label')
9    ax.set_ylabel('Y Label')
10plt.show()
11# Labels and titles overlap — hard to read

Method 1: tight_layout() (Simplest)

plt.tight_layout() automatically adjusts subplot spacing to prevent overlap:

python
1fig, axes = plt.subplots(3, 3, figsize=(10, 8))
2for ax in axes.flat:
3    ax.plot(np.random.randn(50))
4    ax.set_title('Title')
5    ax.set_xlabel('X Label')
6    ax.set_ylabel('Y Label')
7
8plt.tight_layout()
9plt.savefig('subplots.png', dpi=150)
10plt.show()

Add padding between subplots:

python
1plt.tight_layout(pad=2.0)          # Padding around the whole figure
2plt.tight_layout(w_pad=1.0)        # Horizontal padding between subplots
3plt.tight_layout(h_pad=1.5)        # Vertical padding between subplots
4plt.tight_layout(pad=1.0, w_pad=0.5, h_pad=1.0)  # All three

Method 2: subplots_adjust() (Manual Control)

For precise control over all spacing parameters:

python
1fig, axes = plt.subplots(3, 3, figsize=(10, 8))
2for ax in axes.flat:
3    ax.plot(np.random.randn(50))
4    ax.set_title('Title')
5
6fig.subplots_adjust(
7    left=0.1,      # Left edge of subplots (fraction of figure width)
8    right=0.95,    # Right edge
9    top=0.92,      # Top edge
10    bottom=0.08,   # Bottom edge
11    wspace=0.3,    # Width space between subplots
12    hspace=0.4     # Height space between subplots
13)
14plt.show()
ParameterWhat it controlsDefault
leftLeft margin0.125
rightRight margin0.9
topTop margin0.88
bottomBottom margin0.11
wspaceHorizontal gap between subplots0.2
hspaceVertical gap between subplots0.2

Method 3: constrained_layout (Modern)

constrained_layout is a newer alternative to tight_layout that handles more edge cases, including colorbars and legends:

python
1fig, axes = plt.subplots(3, 3, figsize=(10, 8), constrained_layout=True)
2for ax in axes.flat:
3    im = ax.imshow(np.random.rand(10, 10))
4    ax.set_title('Heatmap')
5    fig.colorbar(im, ax=ax, shrink=0.6)
6
7plt.show()
8# Colorbars are properly spaced — tight_layout often fails here

Enable it globally:

python
plt.rcParams['figure.constrained_layout.use'] = True

Method 4: GridSpec (Advanced Layouts)

GridSpec gives full control over subplot sizes and positions:

python
1from matplotlib.gridspec import GridSpec
2
3fig = plt.figure(figsize=(12, 8))
4
5gs = GridSpec(3, 3, figure=fig, wspace=0.4, hspace=0.5)
6
7# Regular grid
8for i in range(3):
9    for j in range(3):
10        ax = fig.add_subplot(gs[i, j])
11        ax.plot(np.random.randn(20))
12        ax.set_title(f'({i},{j})')
13
14plt.show()

Subplots of Different Sizes

python
1fig = plt.figure(figsize=(12, 8))
2gs = GridSpec(3, 3, figure=fig, wspace=0.3, hspace=0.3)
3
4# Large plot spanning 2 rows and 2 columns
5ax_main = fig.add_subplot(gs[0:2, 0:2])
6ax_main.set_title('Main Plot')
7
8# Smaller plots on the right
9ax_right1 = fig.add_subplot(gs[0, 2])
10ax_right2 = fig.add_subplot(gs[1, 2])
11
12# Bottom row
13ax_bottom = fig.add_subplot(gs[2, :])
14ax_bottom.set_title('Full Width Bottom')
15
16plt.tight_layout()
17plt.show()

Method 5: Increase Figure Size

Sometimes the simplest fix is making the figure larger:

python
1# Default size — too cramped for 4x4 subplots
2fig, axes = plt.subplots(4, 4, figsize=(8, 6))
3
4# Better — give each subplot more room
5fig, axes = plt.subplots(4, 4, figsize=(16, 12))
6plt.tight_layout()
7plt.show()

A good rule of thumb: allocate at least 3-4 inches per subplot in each dimension.

Removing Redundant Labels

When subplots share axes, remove inner labels to save space:

python
1fig, axes = plt.subplots(3, 3, figsize=(10, 8), sharex=True, sharey=True)
2for ax in axes.flat:
3    ax.plot(np.random.randn(50))
4
5# Remove inner tick labels
6for ax in axes.flat:
7    ax.label_outer()  # Only keeps labels on outer edges
8
9plt.tight_layout()
10plt.show()

Suptitle with Subplots

fig.suptitle() often overlaps the top row. Fix with top parameter:

python
1fig, axes = plt.subplots(2, 2, figsize=(10, 8))
2fig.suptitle('My Figure Title', fontsize=16)
3
4for ax in axes.flat:
5    ax.plot(np.random.randn(30))
6    ax.set_title('Subplot Title')
7
8plt.tight_layout(rect=[0, 0, 1, 0.95])  # Leave room for suptitle
9plt.show()

Common Pitfalls

  • tight_layout() after all content is added: Call plt.tight_layout() after all subplots, titles, labels, and legends are set. Calling it too early means it calculates spacing without accounting for later additions.
  • constrained_layout + tight_layout conflict: Do not use both. They are mutually exclusive approaches. If constrained_layout=True is set, do not call tight_layout().
  • Colorbars with tight_layout: tight_layout() does not account for colorbars well. Use constrained_layout=True instead, or manually adjust with subplots_adjust().
  • DPI and saved figure size: The spacing that looks good on screen may look different when saved at high DPI. Always check the saved output with plt.savefig('plot.png', dpi=150, bbox_inches='tight').
  • Too many subplots: Beyond ~25 subplots (5x5), individual plots become too small to read regardless of spacing. Consider paginating across multiple figures or using a different visualization approach.

Summary

  • Use plt.tight_layout() as the first fix for overlapping labels — it handles most cases
  • Use constrained_layout=True for figures with colorbars, legends, or complex elements
  • Use fig.subplots_adjust(wspace=, hspace=) for manual fine-tuning of spacing
  • Use GridSpec for non-uniform subplot sizes and advanced layouts
  • Use sharex=True, sharey=True with label_outer() to eliminate redundant tick labels
  • Increase figsize to give each subplot more room — sometimes the simplest solution

Course illustration
Course illustration

All Rights Reserved.