2014-09-12 19 views
36

Qual è la procedura migliore per aggiungere una riga e un'intestazione di colonna a una griglia di sottotrame generata in un ciclo in matplotlib? Mi viene in mente una coppia, ma non particolarmente pulito:Intestazioni di righe e colonne nelle sottotrame di matplotlib

  1. Per le colonne, con un contatore per il ciclo è possibile utilizzare per set_title() solo la prima riga. Per le righe questo non funziona. Dovresti disegnare text al di fuori dei grafici.
  2. Si aggiunge una riga aggiuntiva di sottotrame nella parte superiore e una colonna in più di sottotrame sulla sinistra e si disegna il testo nel mezzo di tale sottotrama.

Potete suggerire un'alternativa migliore?

enter image description here

risposta

35

Ci sono diversi modi per farlo. Il modo più semplice è sfruttare le etichette e i titoli della trama e quindi utilizzare fig.tight_layout() per creare spazio per le etichette. In alternativa, è possibile inserire del testo aggiuntivo nella posizione corretta con annotate e quindi fare spazio per esso manualmente.


Se non si dispone di y-etichette sui vostri assi, è facile da sfruttare il titolo e y-label della prima riga e colonna di assi.

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

enter image description here


Se avete y-etichette, o se si preferisce un po 'più di flessibilità, è possibile utilizzare annotate per posizionare le etichette. Questo è più complicato, ma consente di avere titoli di trama individuali, ylabels, ecc. Oltre alle etichette di righe e colonne.

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

enter image description here

+1

La metodi 'is_first_col()', 'is_last_col()', 'is_first_row()' e 'is_last_row()' può anche essere conveniente in questo contesto. – gerrit

1

I lavori risposta di cui sopra. Basta che non che nella seconda versione della risposta, si ha:

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

invece di:

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