Search code examples
pythonmatplotlibsubplot

Matplotlib align subplot titles to top of figure


Given the following minimal example, how can I make the subplots' titles align vertically (without moving the subplots themselves)? I need the aspect ratio to be set to 'equal' so plots are not stretched. I chose data to be of different scales on purpose.

import numpy as np
import matplotlib.pyplot as plt

data1 = np.random.multivariate_normal(mean=[0,0],cov=np.diag([5,2]),size=50)
data2 = np.random.multivariate_normal(mean=[5,3],cov=np.diag([10,20]),size=50)
data3 = np.random.multivariate_normal(mean=[-8,7],cov=np.diag([1,7]),size=50)

fig, (ax1,ax2,ax3) = plt.subplots(1,3,figsize=(5,2))

ax1.scatter(data1[:,0],data1[:,1])
ax2.scatter(data2[:,0],data2[:,1])
ax3.scatter(data3[:,0],data3[:,1])

ax1.set_aspect('equal')
ax2.set_aspect('equal')
ax3.set_aspect('equal')

ax1.set_title('Title 1')
ax2.set_title('Title 2')
ax3.set_title('Title 3')

plt.show()

enter image description here

Edit: The question has been closed and I'm not sure why. What I'm asking for is to align the titles of multiple subplots without moving the plot themselves. I don't think any of the suggested questions are related to my request.


Solution

  • New in matplotlib 3.9

    There is a new figure method align_titles that automatically aligns subplot titles, so now it's as simple as adding one line:

    fig.align_titles()  # requires matplotlib 3.9+
    

    top-aligned titles via fig.align_titles()


    Prior to matplotlib 3.9

    Use subfigure suptitles to automatically top-align along y and center-align along x:

    1. Create 3 subfigures (requires matplotlib 3.4.0+)
    2. Add a 100% width axes per subfigure
    3. Add a suptitle per subfigure

    The suptitles will then be top-aligned to the figure and center-aligned to the axes (since the axes are 100% width):

    top-aligned titles via subfigures

    fig = plt.figure(constrained_layout=True, figsize=(10, 4))
    
    # create 3 subfigs (width padding=30%)
    sf1, sf2, sf3 = fig.subfigures(1, 3, wspace=0.3)
    
    # add an axes to each subfig (left=0%, bottom=0%, width=100%, height=90%)
    ax1 = sf1.add_axes([0, 0, 1, 0.9])
    ax2 = sf2.add_axes([0, 0, 1, 0.9])
    ax3 = sf3.add_axes([0, 0, 1, 0.9])
    
    ax1.scatter(data1[:, 0], data1[:, 1])
    ax2.scatter(data2[:, 0], data2[:, 1])
    ax3.scatter(data3[:, 0], data3[:, 1])
    
    ax1.set_aspect('equal')
    ax2.set_aspect('equal')
    ax3.set_aspect('equal')
    
    # plot suptitle per subfig
    sf1.suptitle('suptitle 1')
    sf2.suptitle('suptitle 2')
    sf3.suptitle('suptitle 3')
    
    plt.show()