设置 matplotlib 子图的绝对大小

Set absolute size of matplotlib subplots

我知道如何使用 gridspec 或 subplots_adjust 设置图形中子图的相对大小,并且我知道如何使用 figsize 设置图形的大小。我的问题是设置子图的绝对大小。

用例: 我正在制作两个单独的图,它们将保存为学术论文的 pdf 文件。一个有两个子图,一个有三个子图(两种情况都在 1 行中)。我需要 5 个子图中的每一个在生成的 PDF 中具有完全相同的大小和完全相同的字体大小(轴标签、刻度标签等)。在下面的示例中,字体大小相同,但子图不同。如果我使生成的 PDF 的高度相同(因此轴),则 3-subplots.pdf 上的字体小于 2-subplots.pdf.

上的字体

MWE:

import matplotlib.pyplot as plt

subplots = [2, 3]
for i, cols in enumerate(subplots):

    fig, ax = plt.subplots(1, cols, sharey=True, subplot_kw=dict(box_aspect=1))

    for j in range(cols):
        ax[j].set_title(f'plot {j*cols}')
        ax[j].set_xlabel('My x label')
    ax[0].set_ylabel('My y label')

    plt.tight_layout()
    plt.savefig(f'{cols}-subplots.pdf', bbox_inches='tight', pad_inches=0)
    plt.show()

输出

我更喜欢使用 fig.add_axes([left, bottom, width, height]),它可以让您精确控制每个子图的大小和位置。 leftbottom 决定子图的位置,而 widthheight 决定大小。所有数量都是图形宽度和高度的分数,因此它们都在 0 和 1 之间浮动。

一个例子:

fig = plt.figure(figsize=(8.3, 11.7))
axs = {
    "ax1": fig.add_axes([0.2, 0.7, 0.6, 0.2], xticklabels=[]),
    "ax2": fig.add_axes([0.2, 0.49, 0.6, 0.2], xticklabels=[]),
    "ax3": fig.add_axes([0.2, 0.28, 0.6, 0.2]),
}

有了这个,我在一张A4尺寸的图中创建了3个子图,每个子图的宽度为0.6x8.3,高度为0.2x11.7。它们之间的间距为 0.1x11.7。 "ax1""ax2" 不显示 xticklabels 以便我稍后可以为它们设置共享 x 刻度。

您可以查看 matplotlib API 参考以获取更多信息https://matplotlib.org/stable/api/figure_api.html

我最终通过以下方式解决了这个问题:

  1. 设置子图 width/height 的显式绝对长度,子图之间的 space 和子图外部的 space,
  2. 将它们相加得到绝对数字大小,
  3. 将子图 box_aspect 设置为 1 以使其保持正方形。
import matplotlib.pyplot as plt

num_subplots = [2, 3]

scale = 1 # scaling factor for the plot
subplot_abs_width = 2*scale # Both the width and height of each subplot
subplot_abs_spacing_width = 0.2*scale # The width of the spacing between subplots
subplot_abs_excess_width = 0.3*scale # The width of the excess space on the left and right of the subplots
subplot_abs_excess_height = 0.3*scale # The height of the excess space on the top and bottom of the subplots

for i, cols in enumerate(num_subplots):
    fig_width = (cols * subplot_abs_width) + ((cols-1) * subplot_abs_spacing_width) + subplot_abs_excess_width
    fig_height = subplot_abs_width+subplot_abs_excess_height

    fig, ax = plt.subplots(1, cols, sharey=True, figsize=(fig_width, fig_height), subplot_kw=dict(box_aspect=1))

    for j in range(cols):
        ax[j].set_title(f'plot {j}')
        ax[j].set_xlabel('My x label')
    ax[0].set_ylabel('My y label')

    plt.tight_layout()
    plt.savefig(f'{cols}-subplots.pdf', bbox_inches='tight', pad_inches=0)
    plt.show()