Martin
Martin

Reputation: 50

Matplotlib Subfigures: equal figure sizes regardless of ylabel

I want to create a plot containing

Problem: if I use a ylabel for the leftmost plots, the images of the first subfigure get downscaled in order to keep the subfigure sizes identical. If I don't use ylabels, the scaling is correct.

With a ylabel:

With ylabel

Without a ylabel:

Without ylabel

Code:

import matplotlib.pyplot as plt


def example_plot(axis):
    axis.plot([0, 1])
    axis.set_aspect("equal")


if __name__ == "__main__":
    fig = plt.figure(constrained_layout=True)
    fig.suptitle("Title")
    subfigs = fig.subfigures(1, 3)
    for idx, subfig in enumerate(subfigs):
        ax = subfig.subplots(3, 2)
        subfig.suptitle("Subfig title")
        ax[0][0].set_title("Col 1")
        ax[0][1].set_title("Col 2")
        if idx == 0:
            ax[0][0].set_ylabel("Row 1")
            ax[1][0].set_ylabel("Row 2")
            ax[2][0].set_ylabel("Row 3")
        for row in range(3):
            for col in range(2):
                example_plot(ax[row][col])

    plt.show()

Upvotes: 0

Views: 218

Answers (1)

Matt Pitkin
Matt Pitkin

Reputation: 6392

A slightly ugly solution is to just have empty y-labels on the other subfigures, e.g.:

if __name__ == "__main__":
    fig = plt.figure(constrained_layout=True)
    fig.suptitle("Title")
    subfigs = fig.subfigures(1, 3)
    for idx, subfig in enumerate(subfigs):
        ax = subfig.subplots(3, 2)
        subfig.suptitle("Subfig title")
        ax[0][0].set_title("Col 1")
        ax[0][1].set_title("Col 2")
        if idx == 0:
            ax[0][0].set_ylabel("Row 1")
            ax[1][0].set_ylabel("Row 2")
            ax[2][0].set_ylabel("Row 3")
        else:
            for i in range(3):
                ax[i][0].set_ylabel(" ")

        for row in range(3):
            for col in range(2):
                example_plot(ax[row][col])

    plt.show()

Another option is to just set the y-labels as text boxes by working out where they should be, e.g.,

fig = plt.figure(constrained_layout=True)

# get axes object for main figure
axouter = fig.gca()
axouter.axis("off")  # make sure no axes show

fig.suptitle("Title")
subfigs = fig.subfigures(1, 3)
for idx, subfig in enumerate(subfigs):
    ax = subfig.subplots(3, 2)
    subfig.suptitle("Subfig title")
    ax[0][0].set_title("Col 1")
    ax[0][1].set_title("Col 2")
    if idx == 0:
        for i in range(3):
            # set text box within outer axes
            bbox = ax[i][0].get_position().bounds
            if i == 0:
                ypos = bbox[1] + (2/3) * bbox[3]
            else:
                ypos -= (4/3) * bbox[3]
            xpos = bbox[0] - 1.75 * bbox[2]

            axouter.text(xpos, ypos, f"Row {i + 1}", rotation="vertical", va="center_baseline", ha="left")
    for row in range(3):
        for col in range(2):
            example_plot(ax[row][col])

I can't guarantee that the labels will appear where you want them, but you can probably tweak the positions as you need.

Upvotes: 1

Related Questions