subplot titles
matplotlib tutorial
data visualization
coding tips
Python plotting

How to add a title to each subplot

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

Introduction

In Matplotlib, each subplot in a figure can have its own title set with ax.set_title('Title'). The figure itself can have an overall title set with fig.suptitle('Main Title'). When using plt.subplots() to create a grid of axes, iterate over the axes array and call set_title() on each. Proper spacing with plt.tight_layout() or fig.subplots_adjust() prevents titles from overlapping with neighboring subplots.

Basic Subplot Titles

python
1import matplotlib.pyplot as plt
2import numpy as np
3
4fig, axes = plt.subplots(1, 3, figsize=(12, 4))
5
6x = np.linspace(0, 2 * np.pi, 100)
7
8axes[0].plot(x, np.sin(x))
9axes[0].set_title('Sine Wave')
10
11axes[1].plot(x, np.cos(x))
12axes[1].set_title('Cosine Wave')
13
14axes[2].plot(x, np.tan(x))
15axes[2].set_title('Tangent Wave')
16
17plt.tight_layout()
18plt.show()

set_title() places a title centered above each subplot.

Figure Title + Subplot Titles

python
1fig, axes = plt.subplots(2, 2, figsize=(10, 8))
2
3# Overall figure title
4fig.suptitle('Trigonometric Functions', fontsize=16, fontweight='bold')
5
6data = [
7    (np.sin, 'Sine'),
8    (np.cos, 'Cosine'),
9    (np.tan, 'Tangent'),
10    (lambda x: np.sin(x) + np.cos(x), 'Sin + Cos'),
11]
12
13x = np.linspace(0, 2 * np.pi, 100)
14
15for ax, (func, title) in zip(axes.flat, data):
16    ax.plot(x, func(x))
17    ax.set_title(title)
18
19# Adjust spacing so suptitle doesn't overlap subplot titles
20plt.tight_layout(rect=[0, 0, 1, 0.95])
21plt.show()

fig.suptitle() creates a title for the entire figure. The rect parameter in tight_layout() reserves space at the top.

Customizing Title Appearance

python
1fig, axes = plt.subplots(1, 3, figsize=(12, 4))
2
3x = np.linspace(0, 10, 100)
4
5# Font size and weight
6axes[0].plot(x, x**2)
7axes[0].set_title('Quadratic', fontsize=14, fontweight='bold')
8
9# Color and font style
10axes[1].plot(x, np.sqrt(x))
11axes[1].set_title('Square Root', fontsize=14, color='red', fontstyle='italic')
12
13# Position and padding
14axes[2].plot(x, np.log(x + 1))
15axes[2].set_title('Logarithm', fontsize=14, pad=20)  # Extra space above plot
16
17plt.tight_layout()
18plt.show()

Title Alignment

python
1fig, axes = plt.subplots(1, 3, figsize=(12, 4))
2
3axes[0].set_title('Left Aligned', loc='left')
4axes[1].set_title('Center (Default)', loc='center')
5axes[2].set_title('Right Aligned', loc='right')
6
7plt.tight_layout()
8plt.show()

The loc parameter accepts 'left', 'center' (default), or 'right'.

Dynamic Titles in Loops

python
1fig, axes = plt.subplots(2, 3, figsize=(15, 8))
2
3datasets = ['Sales', 'Revenue', 'Profit', 'Customers', 'Orders', 'Returns']
4x = np.arange(12)
5
6for ax, name in zip(axes.flat, datasets):
7    y = np.random.randint(10, 100, size=12)
8    ax.bar(x, y)
9    ax.set_title(f'{name} by Month', fontsize=12)
10
11plt.tight_layout()
12plt.show()

Using axes.flat converts a 2D array of axes into a 1D iterator, making it easy to loop over all subplots.

Titles with plt.subplot (Older API)

python
1plt.figure(figsize=(12, 4))
2
3plt.subplot(1, 3, 1)
4plt.plot([1, 2, 3], [1, 4, 9])
5plt.title('Plot 1')
6
7plt.subplot(1, 3, 2)
8plt.plot([1, 2, 3], [1, 2, 3])
9plt.title('Plot 2')
10
11plt.subplot(1, 3, 3)
12plt.plot([1, 2, 3], [9, 4, 1])
13plt.title('Plot 3')
14
15plt.suptitle('All Three Plots')
16plt.tight_layout()
17plt.show()

plt.title() sets the title for the current subplot when using the stateful pyplot interface.

Seaborn Subplot Titles

Seaborn builds on Matplotlib, so the same set_title() method works:

python
1import seaborn as sns
2import pandas as pd
3
4tips = sns.load_dataset('tips')
5
6fig, axes = plt.subplots(1, 2, figsize=(12, 5))
7
8sns.histplot(data=tips, x='total_bill', ax=axes[0])
9axes[0].set_title('Distribution of Total Bill')
10
11sns.scatterplot(data=tips, x='total_bill', y='tip', ax=axes[1])
12axes[1].set_title('Tip vs Total Bill')
13
14plt.tight_layout()
15plt.show()

FacetGrid Titles

Seaborn's FacetGrid and catplot set titles automatically. Customize them with set_titles():

python
1g = sns.FacetGrid(tips, col='time', row='smoker')
2g.map(sns.histplot, 'total_bill')
3g.set_titles('{col_name} | {row_name}')
4plt.show()

Preventing Title Overlap

python
1fig, axes = plt.subplots(3, 3, figsize=(12, 10))
2
3for i, ax in enumerate(axes.flat):
4    ax.plot([0, 1], [0, 1])
5    ax.set_title(f'Subplot {i + 1}')
6
7# Method 1: tight_layout (automatic)
8plt.tight_layout()
9
10# Method 2: Manual adjustment
11# fig.subplots_adjust(hspace=0.4, wspace=0.3)
12
13# Method 3: constrained_layout (set at figure creation)
14# fig, axes = plt.subplots(3, 3, figsize=(12, 10), constrained_layout=True)
15
16plt.show()

tight_layout() automatically adjusts spacing. For finer control, use subplots_adjust(hspace=..., wspace=...) where hspace is vertical spacing and wspace is horizontal spacing.

Common Pitfalls

  • Titles overlapping with adjacent subplots: Without plt.tight_layout() or constrained_layout=True, subplot titles can overlap with the axes of neighboring rows. Always call tight_layout() before show().
  • Using plt.title() instead of ax.set_title(): plt.title() only works on the current active subplot. When working with the object-oriented API (fig, axes = plt.subplots()), use ax.set_title() to avoid setting the title on the wrong subplot.
  • suptitle hidden behind subplot titles: The figure-level suptitle can overlap with the top row of subplot titles. Use plt.tight_layout(rect=[0, 0, 1, 0.95]) to reserve space at the top.
  • Iterating over axes when it's 2D: plt.subplots(2, 3) returns a 2D array. Use axes.flat to iterate over all axes in a single loop, or axes.flatten() to get a 1D array.
  • plt.subplots(1, 1) returns a single Axes, not an array: With a single subplot, axes is an Axes object, not an array. Use fig, axes = plt.subplots(1, 1) and access axes directly, or use squeeze=False to always get an array.

Summary

  • Use ax.set_title('Title') to add a title to each individual subplot
  • Use fig.suptitle('Title') for an overall figure title
  • Customize titles with fontsize, fontweight, color, loc, and pad parameters
  • Use axes.flat to iterate over all subplots when setting titles in a loop
  • Call plt.tight_layout() to prevent titles from overlapping with adjacent subplots

Related reading
Free course
Beginner
7 lessons
2 hours
Tackling System Design Interview Problems

A short course that equips you with the skills to approach system design interviews methodically.

Start the free course
Track what you have practised

A free account saves your progress, solutions and study plan across every problem on Codemia.

ML System Design practice on Codemia

Design recommenders, ranking systems and training pipelines the way ML interviews actually ask for them, with worked solutions.

Practice ML system design

All Rights Reserved.