【问题标题】:Row titles for matplotlib subplotmatplotlib 子图的行标题
【发布时间】:2015-02-10 03:53:35
【问题描述】:

在matplotlib中,除了为整个图设置的标题和为每个单独的图设置的标题之外,是否可以为每一行子图设置一个单独的标题?这将对应于下图中的橙色文本。

如果没有,您将如何解决这个问题?在左侧创建一个单独的空子图列并用橙色文本填充它们?

我知道可以使用text()annotate() 手动定位每个标题,但这通常需要大量调整,而且我有很多子图。有没有更流畅的解决方案?

【问题讨论】:

    标签: python matplotlib subplot


    【解决方案1】:

    一个想法是创建三个“大子图”,给每个子图一个标题,并使它们不可见。最重要的是,您可以创建较小的子图矩阵。

    这个解决方案完全基于this post,只是更加注重实际去除背景子图。

    import matplotlib.pyplot as plt
    
    fig, big_axes = plt.subplots( figsize=(15.0, 15.0) , nrows=3, ncols=1, sharey=True) 
    
    for row, big_ax in enumerate(big_axes, start=1):
        big_ax.set_title("Subplot row %s \n" % row, fontsize=16)
    
        # Turn off axis lines and ticks of the big subplot 
        # obs alpha is 0 in RGBA string!
        big_ax.tick_params(labelcolor=(1.,1.,1., 0.0), top='off', bottom='off', left='off', right='off')
        # removes the white frame
        big_ax._frameon = False
    
    
    for i in range(1,10):
        ax = fig.add_subplot(3,3,i)
        ax.set_title('Plot title ' + str(i))
    
    fig.set_facecolor('w')
    plt.tight_layout()
    plt.show()
    

    【讨论】:

    • 是的,我看到了这个答案,但觉得必须一直添加隐藏的大子图会有点烦人。也不确定它是否会影响渲染或文件大小。但是看到你的回答,我想这并不太复杂。我建议进行一些编辑以包含情节标题,并在情节之间为两级标题留出适当的垂直空间。
    • 这很好,但破坏了 sharex 和 sharey 选项。
    • 要关闭所有轴线和刻度,只需执行big_ax.axis('off')
    • 不幸的是大多数 matplotlib 格式化方法看起来像 hack...
    • 伙计们,观看big_ax.set_title() 中的\n,我花了一个小时试图理解为什么我的所有标题都在一行
    【解决方案2】:

    从 matplotlib 3.4.0 开始,行标题现在可以实现为 subfigure suptitles

    新的 Figure.subfiguresFigure.add_subfigure 允许在人物中创建虚拟人物 [with] 本地化人物艺术家(例如,颜色条和字幕),这些人物仅适用于每个子人物。

    matplotlib 库包含 how to plot subfigures 上的演示。


    复制OP的参考图:

    • 使用Figure.subfigures(更直接)

      创建fig.subfigures (3x1),其中每个subfig 都有自己的subfig.subplots (1x3) 和subfig.suptitle

      fig = plt.figure(constrained_layout=True)
      fig.suptitle('Figure title')
      
      # create 3x1 subfigs
      subfigs = fig.subfigures(nrows=3, ncols=1)
      for row, subfig in enumerate(subfigs):
          subfig.suptitle(f'Subplot row title {row}')
      
          # create 1x3 subplots per subfig
          axs = subfig.subplots(nrows=1, ncols=3)
          for col, ax in enumerate(axs):
              ax.plot()
              ax.set_title(f'Plot title {col}')
      
    • 或使用Figure.add_subfigure(在现有的pyplot.subplots上)

      如果您已经有一个plt.subplots 图形(3x1),那么将add_subfigures 转换为底层gridspec。同样,每个subfig 都会有自己的subfig.subplots (1x3) 和subfig.suptitle

      # create 3x1 subplots
      fig, axs = plt.subplots(nrows=3, ncols=1, constrained_layout=True)
      fig.suptitle('Figure title')
      
      # clear subplots
      for ax in axs:
          ax.remove()
      
      # add subfigure per subplot
      gridspec = axs[0].get_subplotspec().get_gridspec()
      subfigs = [fig.add_subfigure(gs) for gs in gridspec]
      
      for row, subfig in enumerate(subfigs):
          subfig.suptitle(f'Subplot row title {row}')
      
          # create 1x3 subplots per subfig
          axs = subfig.subplots(nrows=1, ncols=3)
          for col, ax in enumerate(axs):
              ax.plot()
              ax.set_title(f'Plot title {col}')
      

    任一示例的输出(经过一些样式/格式设置):

    【讨论】:

    • 嗨,我收到以下错误 fig = plt.figure(constrained_layout=True) fig.suptitle('Figure title') subfigs = fig.subfigures(nrows=3, ncols=1 ) AttributeError: 'Figure' 对象没有属性 'subfigures'。为什么?
    • @Nachengue 你的matplotlib.__version__ 是什么?子图至少需要 3.4.0
    【解决方案3】:

    另一个简单的作弊方法是把中间一栏的标题写成subplot row XX\n\nPlot title No.YY

    【讨论】:

    • 它不适用于偶数列
    【解决方案4】:

    最好先绘制你的真实子图,然后在它们上面绘制空子图,这样你会有更精确的标题对齐。而要做到这一点,我们需要plt.GridSpec() (link)。

    最好在栏目字幕中看到:

    # modified code of @snake_chrmer
    fig, big_axes = plt.subplots(figsize=(9, 3) , nrows=1, ncols=3, sharey=True) 
    
    for title, big_ax in zip(['First', 'Second', 'Third'], big_axes):
        big_ax.set_title(f'{title}\n', fontweight='semibold')
        big_ax.set_frame_on(False)
        big_ax.axis('off')
    
    
    for i in range(1, 7):
        ax = fig.add_subplot(1,6,i)
        ax.set_title('Plot title ' + str(i))
    
    fig.set_facecolor('w')
    plt.tight_layout()
    plt.show()
    

    # my solition
    import matplotlib.pyplot as plt
    from matplotlib.gridspec import SubplotSpec
    
    def create_subtitle(fig: plt.Figure, grid: SubplotSpec, title: str):
        "Sign sets of subplots with title"
        row = fig.add_subplot(grid)
        # the '\n' is important
        row.set_title(f'{title}\n', fontweight='semibold')
        # hide subplot
        row.set_frame_on(False)
        row.axis('off')
    
    
    rows = 1 
    cols = 6
    fig, axs = plt.subplots(rows, cols, figsize=(9, 3))
    for i, ax in enumerate(axs.flatten()):
        ax.set_title(f'Plot title {i}')
    grid = plt.GridSpec(rows, cols)
    create_subtitle(fig, grid[0, 0:2], 'First')
    create_subtitle(fig, grid[0, 2:4], 'Second')
    create_subtitle(fig, grid[0, 4:6], 'Third')
    fig.tight_layout()
    fig.set_facecolor('w')
    
    

    # original problem
    rows = 3
    cols = 3
    fig, axs = plt.subplots(rows, cols, figsize=(9, 9))
    for i, ax in enumerate(axs.flatten()):
        ax.set_title(f'Plot title {i}')
    grid = plt.GridSpec(rows, cols)
    create_subtitle(fig, grid[0, ::], 'First')
    create_subtitle(fig, grid[1, ::], 'Second')
    create_subtitle(fig, grid[2, ::], 'Third')
    fig.tight_layout()
    fig.set_facecolor('w')
    

    更新

    为一组子图创建子网格只是为了给它们命名更合乎逻辑和易于理解。 subgrig 为修改提供了浪费的空间:

    import matplotlib.pyplot as plt
    import matplotlib.gridspec as gridspec
    
    rows = 1
    cols = 3
    
    fig = plt.figure(figsize=(9, 3))
    # grid for pairs of subplots
    grid = plt.GridSpec(rows, cols)
    
    for i in range(rows * cols):
        # create fake subplot just to title pair of subplots
        fake = fig.add_subplot(grid[i])
        #  '\n' is important
        fake.set_title(f'Fake #{i}\n', fontweight='semibold', size=14)
        fake.set_axis_off()
    
        # create subgrid for two subplots without space between them
        # <https://matplotlib.org/2.0.2/users/gridspec.html>
        gs = gridspec.GridSpecFromSubplotSpec(1, 2, subplot_spec=grid[i], wspace=0)
    
        # real subplot #1
        ax = fig.add_subplot(gs[0])
        ax.set_title(f'Real {i}1')
        # hide ticks and labels
        ax.tick_params(left=False, labelleft=False, labelbottom=False, bottom=False)
    
        # real subplot #2
        ax = fig.add_subplot(gs[1], sharey=ax)
        ax.set_title(f'Real {i}2')
        # hide ticks and labels
        ax.tick_params(left=False, labelleft=False, labelbottom=False, bottom=False)
    
    fig.patch.set_facecolor('white')
    fig.suptitle('SUPERTITLE', fontweight='bold', size=16)
    fig.tight_layout()
    

    原来的问题:

    rows = 3
    cols = 1
    
    fig = plt.figure(figsize=(9, 9))
    # grid for pairs of subplots
    grid = plt.GridSpec(rows, cols)
    
    for i in range(rows * cols):
        # create fake subplot just to title set of subplots
        fake = fig.add_subplot(grid[i])
        # '\n' is important
        fake.set_title(f'Fake #{i}\n', fontweight='semibold', size=14)
        fake.set_axis_off()
    
        # create subgrid for two subplots without space between them
        # <https://matplotlib.org/2.0.2/users/gridspec.html>
        gs = gridspec.GridSpecFromSubplotSpec(1, 3, subplot_spec=grid[i])
    
        # real subplot #1
        ax = fig.add_subplot(gs[0])
        ax.set_title(f'Real {i}1')
    
        # real subplot #2
        ax = fig.add_subplot(gs[1], sharey=ax)
        ax.set_title(f'Real {i}2')
        
        # real subplot #3
        ax = fig.add_subplot(gs[2], sharey=ax)
        ax.set_title(f'Real {i}3')
    
    fig.patch.set_facecolor('white')
    fig.suptitle('SUPERTITLE', fontweight='bold', size=16)
    fig.tight_layout()
    

    【讨论】:

      猜你喜欢
      • 2014-11-06
      • 1970-01-01
      • 2017-06-15
      • 2017-05-21
      • 2018-05-26
      • 2011-11-23
      • 1970-01-01
      • 2023-04-01
      相关资源
      最近更新 更多