I want to create a plot containing
- 3 subfigures (1 row, 3 cols)
- each of these subfigures contains 3x2 subplots with square image plots
- the figure has a title, each subfigure has a title and the first row of subplots also has a title per subplot
- the plots of the first column have a ylabel
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:
Without a 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()
CodePudding user response:
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.