In matplotlib, Is it possible to set a a separate title for each row of subplots in addition to the title set for the entire figure and the title set for each individual plot? This would correspond to the orange text in the figure below. enter image description here

If not, how would you get around this problem? Create a separate column of empty subplots to the left and fill them with the orange text?

I am aware that it is possible to manually position each single title using text() or annotate(), but that usually requires a lot of tweaking and I have many subplots. Is there a smoother solution?

4 Answers

New in matplotlib 3.4.0

Row titles can now be implemented as subfigure suptitles:

The new subfigure feature allows creating virtual figures within figures with localized artists (e.g., colorbars and suptitles) that only pertain to each subfigure.

See how to plot subfigures for further details.


How to reproduce OP's reference figure:

  • Either Figure.subfigures (most straightforward)

    Create 3x1 fig.subfigures where each subfig gets its own 1x3 subfig.subplots and subfig.suptitle:

    fig = plt.figure(constrained_layout=True) fig.suptitle('Figure title') # create 3x1 subfigs subfigs = fig.subfigures(nrows=3, ncols=1) for row, subfig in enumerate(subfigs): subfig.suptitle(f'Subfigure title {row}') # create 1x3 subplots per subfig axs = subfig.subplots(nrows=1, ncols=3) for col, ax in enumerate(axs): ax.plot() ax.set_title(f'Plot title {col}') 
  • Or Figure.add_subfigure (onto existing subplots)

    If you already have 3x1 plt.subplots, then add_subfigure into the underlying gridspec. Again each subfig will get its own 1x3 subfig.subplots and subfig.suptitle:

    # create 3x1 subplots fig, axs = plt.subplots(nrows=3, ncols=1, constrained_layout=True) fig.suptitle('Figure title') # clear subplots for ax in axs: ax.remove() # add subfigure per subplot gridspec = axs[0].get_subplotspec().get_gridspec() subfigs = [fig.add_subfigure(gs) for gs in gridspec] for row, subfig in enumerate(subfigs): subfig.suptitle(f'Subfigure title {row}') # create 1x3 subplots per subfig axs = subfig.subplots(nrows=1, ncols=3) for col, ax in enumerate(axs): ax.plot() ax.set_title(f'Plot title {col}') 

Output of either example (after some styling):

4

An idea is to create three "big subplots", to give each of them a title, and make them invisible. On the top of that you can create your matrix of smaller subplots.

This solution is entirely based on this post, except that more attention has been paid to actually removing the background subplot.

enter image description here

import matplotlib.pyplot as plt fig, big_axes = plt.subplots( figsize=(15.0, 15.0) , nrows=3, ncols=1, sharey=True) for row, big_ax in enumerate(big_axes, start=1): big_ax.set_title("Subplot row %s \n" % row, fontsize=16) # Turn off axis lines and ticks of the big subplot # obs alpha is 0 in RGBA string! big_ax.tick_params(labelcolor=(1.,1.,1., 0.0), top='off', bottom='off', left='off', right='off') # removes the white frame big_ax._frameon = False for i in range(1,10): ax = fig.add_subplot(3,3,i) ax.set_title('Plot title ' + str(i)) fig.set_facecolor('w') plt.tight_layout() plt.show() 
5

Another easy cheat is to give the title of the middle column as subplot row XX\n\nPlot title No.YY

1

It is better to firstly plot your real subplots and then plot empty subplots above them, thus you will have a more precise title align. And to do it precisely we need plt.GridSpec() (link).

It is best seen in columns subtitles:

# modified code of @snake_chrmer fig, big_axes = plt.subplots(figsize=(9, 3) , nrows=1, ncols=3, sharey=True) for title, big_ax in zip(['First', 'Second', 'Third'], big_axes): big_ax.set_title(f'{title}\n', fontweight='semibold') big_ax.set_frame_on(False) big_ax.axis('off') for i in range(1, 7): ax = fig.add_subplot(1,6,i) ax.set_title('Plot title ' + str(i)) fig.set_facecolor('w') plt.tight_layout() plt.show() 

enter image description here

# my solition import matplotlib.pyplot as plt from matplotlib.gridspec import SubplotSpec def create_subtitle(fig: plt.Figure, grid: SubplotSpec, title: str): "Sign sets of subplots with title" row = fig.add_subplot(grid) # the '\n' is important row.set_title(f'{title}\n', fontweight='semibold') # hide subplot row.set_frame_on(False) row.axis('off') rows = 1 cols = 6 fig, axs = plt.subplots(rows, cols, figsize=(9, 3)) for i, ax in enumerate(axs.flatten()): ax.set_title(f'Plot title {i}') grid = plt.GridSpec(rows, cols) create_subtitle(fig, grid[0, 0:2], 'First') create_subtitle(fig, grid[0, 2:4], 'Second') create_subtitle(fig, grid[0, 4:6], 'Third') fig.tight_layout() fig.set_facecolor('w') 

enter image description here

# original problem rows = 3 cols = 3 fig, axs = plt.subplots(rows, cols, figsize=(9, 9)) for i, ax in enumerate(axs.flatten()): ax.set_title(f'Plot title {i}') grid = plt.GridSpec(rows, cols) create_subtitle(fig, grid[0, ::], 'First') create_subtitle(fig, grid[1, ::], 'Second') create_subtitle(fig, grid[2, ::], 'Third') fig.tight_layout() fig.set_facecolor('w') 

enter image description here

UPD

It is more logical and comprehensible to create subgrid for a set of subplots just to title them. The subgrig gives a wast space for modifications:

import matplotlib.pyplot as plt import matplotlib.gridspec as gridspec rows = 1 cols = 3 fig = plt.figure(figsize=(9, 3)) # grid for pairs of subplots grid = plt.GridSpec(rows, cols) for i in range(rows * cols): # create fake subplot just to title pair of subplots fake = fig.add_subplot(grid[i]) # '\n' is important fake.set_title(f'Fake #{i}\n', fontweight='semibold', size=14) fake.set_axis_off() # create subgrid for two subplots without space between them # < gs = gridspec.GridSpecFromSubplotSpec(1, 2, subplot_spec=grid[i], wspace=0) # real subplot #1 ax = fig.add_subplot(gs[0]) ax.set_title(f'Real {i}1') # hide ticks and labels ax.tick_params(left=False, labelleft=False, labelbottom=False, bottom=False) # real subplot #2 ax = fig.add_subplot(gs[1], sharey=ax) ax.set_title(f'Real {i}2') # hide ticks and labels ax.tick_params(left=False, labelleft=False, labelbottom=False, bottom=False) fig.patch.set_facecolor('white') fig.suptitle('SUPERTITLE', fontweight='bold', size=16) fig.tight_layout() 

enter image description here

Original problem:

rows = 3 cols = 1 fig = plt.figure(figsize=(9, 9)) # grid for pairs of subplots grid = plt.GridSpec(rows, cols) for i in range(rows * cols): # create fake subplot just to title set of subplots fake = fig.add_subplot(grid[i]) # '\n' is important fake.set_title(f'Fake #{i}\n', fontweight='semibold', size=14) fake.set_axis_off() # create subgrid for two subplots without space between them # < gs = gridspec.GridSpecFromSubplotSpec(1, 3, subplot_spec=grid[i]) # real subplot #1 ax = fig.add_subplot(gs[0]) ax.set_title(f'Real {i}1') # real subplot #2 ax = fig.add_subplot(gs[1], sharey=ax) ax.set_title(f'Real {i}2') # real subplot #3 ax = fig.add_subplot(gs[2], sharey=ax) ax.set_title(f'Real {i}3') fig.patch.set_facecolor('white') fig.suptitle('SUPERTITLE', fontweight='bold', size=16) fig.tight_layout() 

enter image description here

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy