HOWTO · Matplotlib

How to Add Title to Subplots in Matplotlib

We can add a title to subplots in Matplotlib using Axes.set_title(), Axes.title.set_text(), or pyplot's current axes (plt.gca()).

On this page

To give each subplot its own title, call Axes.set_title() on the corresponding axes. This is the clearest choice when you create several plots together because each title stays associated with the axes it describes. A subplot title belongs to one axes; use a figure-level title such as fig.suptitle() when one heading should describe the whole figure.

Add subplot titles with Axes.set_title()

plt.subplots() returns a figure and an array of axes when squeeze=False is set. The example below plots four series, pairs each axes with a descriptive label, and applies the layout before displaying the figure. This keeps axes two-dimensional for a 2×2 grid, a single row (1×N), a single column (N×1), or a single subplot (1×1). For a different grid size, adjust the plt.subplots() arguments and provide one title for every axes.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
series = [np.sin(x), np.cos(x), np.sin(2 * x), np.cos(2 * x)]
titles = ["Sine", "Cosine", "Sine, double frequency", "Cosine, double frequency"]

fig, axes = plt.subplots(2, 2, figsize=(8, 5), squeeze=False)
for ax, y, title in zip(axes.flat, series, titles):
    ax.plot(x, y)
    ax.set_title(title)

fig.tight_layout()
plt.show()

A 2×2 grid of subplots titled with Axes.set_title(): Sine, Cosine, Sine double frequency, and Cosine double frequency, each title centered above its own curve.

Because squeeze=False keeps the axes in a two-dimensional array even for a 1×1 grid, axes.flat iterates consistently over all four grid shapes: 2×2, 1×N, N×1, and 1×1. zip() pairs each axes and data series with a title, so keep those collections in the same order and length.

The label argument is the text shown above an axes. You can also pass options such as fontsize or loc to control the title’s appearance and placement. Keep long titles concise; if adjacent titles or labels have little room, increase the figure size or adjust spacing before display. tight_layout() is a useful starting point, although a layout with many decorations may need additional spacing adjustments.

Set subplot titles with Axes.title.set_text()

An axes also exposes its title as a text object. Use Axes.title.set_text() to set or update that text after the axes exists. This can be convenient when a program already keeps a reference to the title object or changes the wording later. For initial plot setup, Axes.set_title() is generally more direct and makes the operation clear at the axes call site.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)

fig, axes = plt.subplots(1, 2, figsize=(8, 3.5), squeeze=False)
axes[0, 0].plot(x, np.sin(x))
axes[0, 1].plot(x, np.cos(x))

axes[0, 0].title.set_text("Sine")
axes[0, 1].title.set_text("Cosine")

fig.tight_layout()
plt.show()

Two subplots side by side whose titles were set with Axes.title.set_text(): Sine above the sine curve and Cosine above the cosine curve.

For example, after creating an axes named ax, call ax.title.set_text("Updated title"). This updates that axes title; it does not set a title for every subplot in the figure. With multiple axes, select the one to update explicitly, or loop over the axes and their corresponding strings as in the first example. Both APIs address the axes title, so choose one style for a given operation rather than setting the same title twice.

Set subplot titles with pyplot’s current axes

When using pyplot’s state-based interface, plt.gca() returns the current axes. You can set its title with plt.gca().set_title("Title"); the equivalent text-object form is plt.gca().title.set_text("Title"). The parentheses matter: gca() is a function call that retrieves the current axes. These forms affect only the axes that is current at that moment, so they are useful when plotting one axes at a time.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)
series = [np.sin(x), np.cos(x), np.sin(2 * x), np.cos(2 * x)]
titles = ["Sine", "Cosine", "Sine, double frequency", "Cosine, double frequency"]

plt.figure(figsize=(8, 5))
for position, (y, title) in enumerate(zip(series, titles), start=1):
    plt.subplot(2, 2, position)
    plt.plot(x, y)
    plt.gca().set_title(title)

plt.tight_layout()
plt.show()

The same 2×2 grid of sine and cosine curves, built in a loop with plt.subplot() and plt.gca().set_title(), with one title above each subplot.

For a grid, select each subplot before setting its title. For example, plt.subplot(2, 2, position) selects a position from one through four, after which plt.gca().set_title(title) applies to that selected axes. If you create the grid with fig, axes = plt.subplots(...), using the returned axes references is usually easier to follow and less dependent on pyplot’s current state.

Finish configuring the figure before calling plt.show(). In particular, call plt.tight_layout() before display so the layout can account for subplot titles.

Fix overlapping or clipped titles and missing titles

A title is centered over its axes, so a long title can be wider than the axes itself. With the two titles used in the example below on a figure of size (8, 3.5), the measured title boxes overlap by 15 pixels when no layout adjustment is applied, so the end of one title runs into the start of the next. Calling fig.tight_layout() separates them — the same measurement then leaves a 39-pixel gap — but tight_layout() only redistributes the space between subplots; it does not shrink the title text. At that figure size, the second title then extends 11 pixels past the right edge of the figure and is clipped in the rendered output. The complete fix is to give the titles room: keep tight_layout() and widen the figure, as in this example, or shorten the titles. Reducing the title fontsize to 11 at the original size also brings both titles fully inside the figure.

import numpy as np
import matplotlib.pyplot as plt

x = np.linspace(0, 2 * np.pi, 100)

fig, axes = plt.subplots(1, 2, figsize=(9, 3.5), squeeze=False)
axes[0, 0].plot(x, np.sin(x))
axes[0, 1].plot(x, np.cos(x))
axes[0, 0].set_title("Sine wave measured at the first sensor")
axes[0, 1].set_title("Cosine wave measured at the second sensor")

fig.tight_layout()
plt.show()

Two subplots with the long titles Sine wave measured at the first sensor and Cosine wave measured at the second sensor, fully visible and separated after widening the figure and applying tight_layout().

At size (9, 3.5), both titles end inside the figure with space between them, and no text is cut off. A missing title fails silently instead: the loops in this article pair axes with titles through zip(), and zip() stops at the shorter sequence without raising an error. Supplying three titles for the four subplots of a 2×2 grid leaves the fourth axes with an empty title — reading it back with ax.get_title() returns an empty string — and nothing warns you. When one subplot appears without a title, count the titles against the axes before checking the title calls themselves.