Nagłówki wierszy i kolumn na wykresach podrzędnych matplotlib

88

Jaka jest najlepsza praktyka dodawania wiersza i nagłówka kolumny do siatki wykresów pobocznych generowanych w pętli matplotlib? Przychodzi mi na myśl para, ale niezbyt schludna:

  1. W przypadku kolumn, z licznikiem do pętli, możesz użyć tylko set_title()dla pierwszego wiersza. W przypadku rzędów to nie działa. Musiałbyś rysować textpoza działkami.
  2. Dodajesz dodatkowy rząd wykresów cząstkowych u góry i dodatkową kolumnę wykresów pobocznych po lewej i rysujesz tekst w środku tego wykresu.

Czy możesz zaproponować lepszą alternatywę?

wprowadź opis obrazu tutaj

gozzilli
źródło

Odpowiedzi:

116

Można to zrobić na kilka sposobów. Najłatwiejszym sposobem jest wykorzystanie etykiet y i tytułów fabuły, a następnie użycie ich fig.tight_layout()do zrobienia miejsca na etykiety. Alternatywnie możesz umieścić dodatkowy tekst we właściwym miejscu za pomocą, annotatea następnie zrobić na niego miejsce półautomatycznie.


Jeśli nie masz etykiet y na swoich osiach, łatwo jest wykorzystać tytuł i etykietę y pierwszego wiersza i kolumny osi.

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

wprowadź opis obrazu tutaj


Jeśli masz etykiety y lub wolisz nieco większą elastyczność, możesz użyć ich annotatedo umieszczenia. Jest to bardziej skomplikowane, ale pozwala mieć indywidualne tytuły działek, etykiety itp. Oprócz etykiet wierszy i kolumn.

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

wprowadź opis obrazu tutaj

Joe Kington
źródło
8
Sposoby is_first_col(), is_last_col(), is_first_row()i is_last_row()mogą również być korzystne w tym kontekście.
gerrit
1
Uwaga: adnotacja matplotlib ma opcję obracania, więc jeśli chcesz obrócić etykietę o 90 stopni, po prostu dodaj argumentrotation = 90
mathishard.butweloveit
2

Powyższa odpowiedź działa. Tylko nie to, że w drugiej wersji odpowiedzi masz:

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

zamiast:

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')
Alan Shteyman
źródło