设置 matplotlib 子图的绝对大小

我知道如何使用 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()

输出output

stack overflow Set absolute size of matplotlib subplots
原文答案
author avatar

接受的答案

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

  1. 为子图宽度/高度、子图之间的空间和子图外的空间设置明确的绝对长度,
    2.将它们相加以获得绝对图形大小,
  2. 将 subplot box_aspect 设置为 1 以保持正方形。

    
    import matplotlib.pyplot as plt

num_subplots = [2, 3]

scale = 1 # scaling factor for the plot
subplot_abs_width = 2scale # 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.3scale # 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()


 [![Picture of solution](https://i.stack.imgur.com/aCYm3.png) ](https://i.stack.imgur.com/aCYm3.png)

答案:

作者头像

我更喜欢使用 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

作者头像

我创建了一个函数,它创建具有绝对大小的轴,并以 plt.subplots(...) 之类的大多数方式起作用,例如通过允许共享 y 轴或 x 轴并将轴作为成形的 numpy 数组返回。假设您将 figsize 设置得足够大,它将轴在其网格区域内居中,在它们自身和图形边缘之间提供尽可能多的空间。

参数包括图形的绝对高度和宽度(有关详细信息,请参阅 matplotlib documentation )以及轴的绝对高度和宽度,如原始问题中所要求的那样。

from typing import Tuple
from matplotlib import pyplot as plt
import numpy as np

def subplots_with_absolute_sized_axes(
        nrows: int, ncols: int,
        figsize: Tuple[float, float],
        axis_width: float, axis_height: float,
        sharex: bool=False, sharey: bool=False) -> Tuple[plt.Figure, numpy.ndarray]:
    ''' Create axes with exact sizes.

    Spaces axes as far from each other and the figure edges as possible
    within the grid defined by nrows, ncols, and figsize.

    Allows you to share y and x axes, if desired.
    '''
    fig = plt.figure(figsize=figsize)
    figwidth, figheight = figsize
    # spacing on each left and right side of the figure
    h_margin = (figwidth - (ncols * axis_width)) / figwidth / ncols / 2
    # spacing on each top and bottom of the figure
    v_margin = (figheight - (nrows * axis_height)) / figheight / nrows / 2
    row_addend = 1 / nrows
    col_addend = 1 / ncols
    inner_ax_width = axis_width / figwidth
    inner_ax_height = axis_height / figheight
    axes = []
    sharex_ax = None
    sharey_ax = None
    for row in range(nrows):
        bottom = (row * row_addend) + v_margin
        for col in range(ncols):
            left = (col * col_addend) + h_margin
            if not axes:
                axes.append(fig.add_axes(
                    [left, bottom, inner_ax_width, inner_ax_height]))
                if sharex:
                    sharex_ax = axes[0]
                if sharey:
                    sharey_ax = axes[0]
            else:
                axes.append(fig.add_axes(
                    [left, bottom, inner_ax_width, inner_ax_height],
                    sharex=sharex_ax, sharey=sharey_ax))
    return fig, np.flip(np.asarray(list(axes)).reshape((nrows, ncols)), axis=0)