matplotlib的子图中的行和列标题

goz*_*lli 63 python matplotlib subplot

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

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

你能建议一个更好的选择吗?

在此输入图像描述

Joe*_*ton 78

有几种方法可以做到这一点.简单的方法是利用图表的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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述


如果您有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()
Run Code Online (Sandbox Code Playgroud)

在此输入图像描述

  • 在这种情况下,方法`is_first_col()`,`is_last_col()`,`is_first_row()`和`is_last_row()`也可能很方便. (5认同)
  • 另请注意,annotate matplotlib 确实有旋转选项,因此如果您想将标签旋转 90 度,只需添加参数 `rotation = 90` (2认同)

pai*_*ime 6

根据 Joe Kington 的回答,我提出了一个可以在代码库中重用的函数:

\n

它接受以下参数:

\n
    \n
  • fig:包含要处理的轴的图形
  • \n
  • row_headers, col_headers:作为标题的字符串序列
  • \n
  • row_pad, col_pad:int调整填充的值
  • \n
  • rotate_row_headers: 是否将行标题旋转90\xc2\xb0
  • \n
  • **text_kwargs: 转发给ax.annotate(...)
  • \n
\n

函数在这里,例子如下:

\n
import numpy as np\n\ndef add_headers(\n    fig,\n    *,\n    row_headers=None,\n    col_headers=None,\n    row_pad=1,\n    col_pad=5,\n    rotate_row_headers=True,\n    **text_kwargs\n):\n    # Based on https://stackoverflow.com/a/25814386\n\n    axes = fig.get_axes()\n\n    for ax in axes:\n        sbs = ax.get_subplotspec()\n\n        # Putting headers on cols\n        if (col_headers is not None) and sbs.is_first_row():\n            ax.annotate(\n                col_headers[sbs.colspan.start],\n                xy=(0.5, 1),\n                xytext=(0, col_pad),\n                xycoords="axes fraction",\n                textcoords="offset points",\n                ha="center",\n                va="baseline",\n                **text_kwargs,\n            )\n\n        # Putting headers on rows\n        if (row_headers is not None) and sbs.is_first_col():\n            ax.annotate(\n                row_headers[sbs.rowspan.start],\n                xy=(0, 0.5),\n                xytext=(-ax.yaxis.labelpad - row_pad, 0),\n                xycoords=ax.yaxis.label,\n                textcoords="offset points",\n                ha="right",\n                va="center",\n                rotation=rotate_row_headers * 90,\n                **text_kwargs,\n            )\n
Run Code Online (Sandbox Code Playgroud)\n

这是在标准网格上使用它的示例(没有轴跨越多行/列):

\n
import random\nimport matplotlib.pyplot as plt\n\nmosaic = [\n    ["A0", "A1", "A2"],\n    ["B0", "B1", "B2"],\n]\nrow_headers = ["Row A", "Row B"]\ncol_headers = ["Col 0", "Col 1", "Col 2"]\n\nsubplots_kwargs = dict(sharex=True, sharey=True, figsize=(10, 6))\nfig, axes = plt.subplot_mosaic(mosaic, **subplots_kwargs)\n\nfont_kwargs = dict(fontfamily="monospace", fontweight="bold", fontsize="large")\nadd_headers(fig, col_headers=col_headers, row_headers=row_headers, **font_kwargs)\n\nplt.show()\n
Run Code Online (Sandbox Code Playgroud)\n

结果:规则网格

\n

如果某些轴跨越多个行/列,则正确分配行/列标题会变得不太简单。\n我没有设法从函数内部对其进行排序,但注意给定的 和 参数row_headerscol_headers足以让它轻松工作:

\n
mosaic = [\n    ["A0", "A1", "A1", "A2"],\n    ["A0", "A1", "A1", "A2"],\n    ["B0", "B1", "B1", "B2"],\n]\n\nrow_headers = ["A", "A", "B"]  # or\nrow_headers = ["A", None, "B"]  # or\nrow_headers = {0: "A", 2: "B"}\n\ncol_headers = ["0", "1", "1", "2"]  # or\ncol_headers = ["0", "1", None, "2"]  # or\ncol_headers = {0: "0", 1: "1", 3: "2"}\n\nfig, axes = plt.subplot_mosaic(mosaic, **subplots_kwargs)\nadd_headers(fig, col_headers=col_headers, row_headers=row_headers, **font_kwargs)\nplt.show()\n
Run Code Online (Sandbox Code Playgroud)\n

结果:非规则网格

\n