【问题标题】:Row and column headers in matplotlib's subplotsmatplotlib 子图中的行和列标题
【发布时间】:2014-11-06 20:48:28
【问题描述】:

matplotlib 的循环中生成的子图网格中添加行和列标题的最佳做法是什么?我能想到一对,但不是特别整齐:

  1. 对于列,使用循环计数器,您可以将set_title() 仅用于第一行。对于行,这不起作用。您必须在绘图之外绘制text
  2. 您在顶部添加额外的一行子图,在左侧添加一列额外的子图,并在该子图的中间绘制文本。

你能推荐一个更好的选择吗?

【问题讨论】:

    标签: python matplotlib subplot


    【解决方案1】:

    有几种方法可以做到这一点。简单的方法是利用绘图的 y 标签和标题,然后使用fig.tight_layout() 为标签腾出空间。或者,您可以使用annotate 在正确的位置放置其他文本,然后半手动为其腾出空间。


    如果轴上没有 y 标签,则很容易利用轴的第一行和第一列的标题和 y 标签。

    import matplotlib.pyplot as plt
    
    cols = ['Column {}'.format(col) for col in range(1, 4)]
    rows = ['Row {}'.format(row) for row in ['A', 'B', 'C', 'D']]
    
    fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(12, 8))
    
    for ax, col in zip(axes[0], cols):
        ax.set_title(col)
    
    for ax, row in zip(axes[:,0], rows):
        ax.set_ylabel(row, rotation=0, size='large')
    
    fig.tight_layout()
    plt.show()
    


    如果您确实有 y 标签,或者您希望更灵活一点,您可以使用annotate 来放置标签。这更复杂,但除了行和列标签之外,您还可以拥有单独的绘图标题、ylabels 等。

    import matplotlib.pyplot as plt
    from matplotlib.transforms import offset_copy
    
    
    cols = ['Column {}'.format(col) for col in range(1, 4)]
    rows = ['Row {}'.format(row) for row in ['A', 'B', 'C', 'D']]
    
    fig, axes = plt.subplots(nrows=4, ncols=3, figsize=(12, 8))
    plt.setp(axes.flat, xlabel='X-label', ylabel='Y-label')
    
    pad = 5 # in points
    
    for ax, col in zip(axes[0], cols):
        ax.annotate(col, xy=(0.5, 1), xytext=(0, pad),
                    xycoords='axes fraction', textcoords='offset points',
                    size='large', ha='center', va='baseline')
    
    for ax, row in zip(axes[:,0], rows):
        ax.annotate(row, xy=(0, 0.5), xytext=(-ax.yaxis.labelpad - pad, 0),
                    xycoords=ax.yaxis.label, textcoords='offset points',
                    size='large', ha='right', va='center')
    
    fig.tight_layout()
    # tight_layout doesn't take these labels into account. We'll need 
    # to make some room. These numbers are are manually tweaked. 
    # You could automatically calculate them, but it's a pain.
    fig.subplots_adjust(left=0.15, top=0.95)
    
    plt.show()
    

    【讨论】:

    • 方法is_first_col()is_last_col()is_first_row()is_last_row()在这种情况下也可能很方便。
    • 另外作为注释,annotate matplotlib 确实有旋转选项,所以如果你想将标签旋转 90 度,只需添加参数rotation = 90
    • 请问,figsize = (12, 8) 中的数字 8 是做什么用的?
    • @Amirhosein 8 是图形的高度,以英寸为单位,12 是宽度,因此 (12,8) 确保了 4x3 的纵横比。因为有 3x3 的子图,这会产生合理的横向子图布局。
    【解决方案2】:

    以上答案有效。只是在答案的第二个版本中,您有:

    for ax, row in zip(axes[:,0], rows):
        ax.annotate(col, xy=(0, 0.5), xytext=(-ax.yaxis.labelpad-pad,0),
                    xycoords=ax.yaxis.label, textcoords='offset points',
                    size='large', ha='right', va='center')
    

    代替:

    for ax, row in zip(axes[:,0], rows):
        ax.annotate(row,xy=(0, 0.5), xytext=(-ax.yaxis.labelpad-pad,0),                    
                    xycoords=ax.yaxis.label, textcoords='offset points',
                    size='large', ha='right', va='center')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-02-10
      • 2017-06-15
      • 2011-11-23
      • 1970-01-01
      • 2017-05-21
      • 2021-09-03
      • 1970-01-01
      相关资源
      最近更新 更多