【问题标题】:Set absolute size of matplotlib subplots设置 matplotlib 子图的绝对大小
【发布时间】:2022-10-13 23:18:25
【问题描述】:

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

输出

【问题讨论】:

    标签: python matplotlib subplot


    【解决方案1】:

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

    【讨论】:

    • 这仍然是相对大小,但确实让我走上了正确的轨道!我将在下面发布我的解决方案。感谢回复。
    【解决方案2】:

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

    1. 为子图宽度/高度、子图之间的空间和子图外的空间设置显式绝对长度,
    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()
      

    【讨论】:

      【解决方案3】:

      我创建了一个函数,它创建具有绝对大小的轴,并以大多数方式起作用,例如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)
      

      【讨论】:

        猜你喜欢
        • 2017-05-22
        • 2019-04-30
        • 1970-01-01
        • 1970-01-01
        • 2015-07-15
        • 1970-01-01
        • 1970-01-01
        • 2011-11-18
        • 2017-12-12
        相关资源
        最近更新 更多