Заголовки строк и столбцов в подзаголовках matplotlib

88

Как лучше всего добавить строку и заголовок столбца в сетку подзаголовков, созданную в цикле matplotlib? Я могу вспомнить пару, но не особо изящную:

  1. Для столбцов со счетчиком цикла вы можете использовать set_title()только первую строку. Для строк это не работает. Придется рисовать textвне сюжетов.
  2. Вы добавляете дополнительную строку подзаголовков вверху и дополнительный столбец подзаголовков слева и рисуете текст в середине этого подзаголовка.

Вы можете предложить лучшую альтернативу?

введите описание изображения здесь

Gozzilli
источник

Ответы:

116

Есть несколько способов сделать это. Самый простой способ - использовать 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для размещения меток. Это более сложно, но позволяет вам иметь отдельные заголовки графика, ярлыки и т. Д. В дополнение к ярлыкам строк и столбцов.

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()

введите описание изображения здесь

Джо Кингтон
источник
8
Методы is_first_col(), is_last_col(), is_first_row()а is_last_row()также могут быть удобны в этом контексте.
gerrit
1
Также в качестве примечания: у annotate matplotlib есть опция поворота, поэтому, если вы хотите повернуть этикетку на 90 градусов, просто добавьте аргументrotation = 90
mathishard.butweloveit
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')
Алан Штейман
источник