Xlabel / ylabel comune per le sottotrame matplotlib


143

Ho la seguente trama:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

e ora vorrei dare a questa trama etichette dell'asse x ed etichette dell'asse y comuni. Con "comune", intendo che ci dovrebbe essere una grande etichetta dell'asse x sotto l'intera griglia di sottotrame e una grande etichetta dell'asse y sulla destra. Non riesco a trovare nulla al riguardo nella documentazione per plt.subplots, e i miei googlings suggeriscono che devo plt.subplot(111)iniziare con un grande , ma come faccio a inserire le mie sottotrame 5 * 2 in quell'uso plt.subplots?


2
Con l'aggiornamento alla domanda e i commenti lasciati nelle risposte sottostanti questo è un duplicato di stackoverflow.com/questions/6963035/…
Hooked

Non proprio, dal momento che la mia domanda è per plt.subplots () e la domanda a cui ti colleghi utilizza add_subplot - Non posso usare quel metodo se non passo a add_subplot, che vorrei evitare. Ho potuto usare la soluzione plt.text che è dato come soluzione alternativa nel tuo link, ma non è la soluzione più elegante.
jolindbe,

Per elaborare, per quanto ho capito, plt.subplots non può generare una serie di sottotrame all'interno di un ambiente di assi esistente, ma crea sempre una nuova figura. Destra?
jolindbe,

Una soluzione più elegante può essere trovato qui: stackoverflow.com/questions/6963035/...
Mr.H

Il tuo link è stato fornito dall'utente Hooked più di 4 anni fa (solo alcuni commenti sopra il tuo). Come ho già detto, quella soluzione riguarda add_subplot e non plt.subplots ().
jolindbe,

Risposte:


206

Sembra quello che vuoi davvero. Applica lo stesso approccio di questa risposta al tuo caso specifico:

import matplotlib.pyplot as plt

fig, ax = plt.subplots(nrows=3, ncols=3, sharex=True, sharey=True, figsize=(6, 6))

fig.text(0.5, 0.04, 'common X', ha='center')
fig.text(0.04, 0.5, 'common Y', va='center', rotation='vertical')

Grafici multipli con etichetta assi comune


4
si noti che 0,5 per la coordinata x dell'etichetta x non colloca l'etichetta al centro della sottotrama centrale. dovresti andare leggermente più grande di quello per tenere conto degli yticklabels.
dbliss,

2
Dai un'occhiata a questa risposta per un metodo che non utilizza plt.text. È possibile creare i grafici secondari, ma quindi aggiungere un grafico a bit, renderlo invisibile ed etichettarlo con xe y.
James Owers,

Grazie, ha funzionato in generale. Qualche soluzione alla rottura durante l'utilizzo tight_layout?
serv-inc,

3
@ serv-inc con tight_layoutsostituzione 0.04con 0sembra funzionare.
divenex,

3
L'uso fig.textnon è una buona idea. Questo rovina cose comeplt.tight_layout()
Peaceful

55

Dal momento che lo considero abbastanza pertinente ed elegante (non è necessario specificare le coordinate per posizionare il testo), copio (con un leggero adattamento) una risposta a un'altra domanda correlata .

import matplotlib.pyplot as plt
fig, axes = plt.subplots(5, 2, sharex=True, sharey=True, figsize=(6,15))
# add a big axis, hide frame
fig.add_subplot(111, frameon=False)
# hide tick and tick label of the big axis
plt.tick_params(labelcolor='none', top=False, bottom=False, left=False, right=False)
plt.xlabel("common X")
plt.ylabel("common Y")

Ciò si traduce nel seguente (con matplotlib versione 2.2.0):

Sottotrame a 5 righe e 2 colonne con etichette comuni degli assi x e y


4
Per semplicità questa dovrebbe essere la risposta accettata. Molto semplice. Rilevante per matplotlib v3.x.
Kyle Swanson,

Vorrei sapere come potrebbe essere utilizzato con più oggetti figura? fig.xlabel ("pippo") non funziona.
Horror Vacui,

FYI: Ora che le persone usano temi scuri in StackOverflow, le etichette possono essere appena lette, quindi è meglio esportare i tuoi png con sfondo bianco
xyzzyqed

@xyzzyqed Non sapevo che esistesse qualcosa come "temi" in StackOverflow e non ricordo nemmeno come ho esportato la figura. Come posso controllare lo sfondo durante l'esportazione?
bli

2
L'unico problema di questa soluzione è che non funziona quando viene utilizzato constrained_layout=Trueperché crea etichette sovrapposte. In questo caso, è necessario regolare manualmente i bordi dei grafici secondari.
baccandr,

35

Senza di sharex=True, sharey=Truete ottieni:

inserisci qui la descrizione dell'immagine

Con esso dovresti renderlo più bello:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))

plt.tight_layout()

inserisci qui la descrizione dell'immagine

Ma se vuoi aggiungere altre etichette, dovresti aggiungerle solo ai grafici dei bordi:

fig, axes2d = plt.subplots(nrows=3, ncols=3,
                           sharex=True, sharey=True,
                           figsize=(6,6))

for i, row in enumerate(axes2d):
    for j, cell in enumerate(row):
        cell.imshow(np.random.rand(32,32))
        if i == len(axes2d) - 1:
            cell.set_xlabel("noise column: {0:d}".format(j + 1))
        if j == 0:
            cell.set_ylabel("noise row: {0:d}".format(i + 1))

plt.tight_layout()

inserisci qui la descrizione dell'immagine

L'aggiunta di un'etichetta per ogni trama lo rovinerebbe (forse c'è un modo per rilevare automaticamente le etichette ripetute, ma non ne sono consapevole).


Questo è molto più difficile se, ad esempio, il numero di grafici non è noto (ad es. Hai una funzione di diagramma generalizzato che funziona per qualsiasi numero di grafici secondari).
naught101

15

Dal momento che il comando:

fig,ax = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

hai usato restituisce una tupla composta dalla figura e da un elenco delle istanze degli assi, è già sufficiente fare qualcosa del genere (attenzione che ho cambiato fig,axin fig,axes):

fig,axes = plt.subplots(5,2,sharex=True,sharey=True,figsize=fig_size)

for ax in axes:
    ax.set_xlabel('Common x-label')
    ax.set_ylabel('Common y-label')

Se ti capita di voler cambiare alcuni dettagli su una sottotrama specifica, puoi accedervi da axes[i]dove iscorre le tue sottotrame.

Potrebbe anche essere molto utile includere a

fig.tight_layout()

alla fine del file, prima di plt.show(), al fine di evitare la sovrapposizione di etichette.


5
Mi dispiace di essere stato un po 'poco chiaro sopra. Con "comune" intendevo una singola etichetta x sotto tutti i grafici e una singola etichetta y a sinistra dei grafici, ho aggiornato la domanda per riflettere questo.
jolindbe,

2
@JohanLindberg: Per quanto riguarda i tuoi commenti qui e sopra: Indeed plt.subplots()creerà una nuova istanza di figure. Se vuoi attenersi a questo comando, puoi facilmente aggiungere un big_ax = fig.add_subplot(111), poiché hai già una figura e puoi aggiungere un altro asse. Successivamente, puoi manipolare big_axil modo in cui viene mostrato nel collegamento da Hooked.
Marius,

Grazie per i tuoi suggerimenti, ma se lo faccio, devo aggiungere il big_ax dopo plt.subplots () e ottengo quel sottotrama in cima a tutto il resto - posso renderlo trasparente o inviarlo in qualche modo indietro? Anche se non ho impostato tutti i colori su nessuno come nel collegamento di Hooked, è ancora una scatola bianca che copre tutti i miei sottotrame.
jolindbe,

2
@JohanLindberg, hai ragione, non avevo verificato. Ma puoi facilmente impostare il colore di sfondo del grande asse nonefacendo: big_ax.set_axis_bgcolor('none')Dovresti anche creare il colore dell'etichetta none(al contrario dell'esempio collegato da Hooked):big_ax.tick_params(labelcolor='none', top='off', bottom='off', left='off', right='off')
Marius

2
Ottengo un errore: AttributeError: 'numpy.ndarray' object has no attribute 'set_xlabel'nella dichiarazione ax.set_xlabel('Common x-label'). Riesci a capirlo?
hengxin,

5

Avrà un aspetto migliore se riservi spazio per le etichette comuni creando etichette invisibili per la sottotrama nell'angolo in basso a sinistra. È anche utile passare alla dimensione del carattere da rcParams. In questo modo, le etichette comuni cambieranno dimensione con la vostra configurazione rc e anche gli assi saranno regolati per lasciare spazio per le etichette comuni.

fig_size = [8, 6]
fig, ax = plt.subplots(5, 2, sharex=True, sharey=True, figsize=fig_size)
# Reserve space for axis labels
ax[-1, 0].set_xlabel('.', color=(0, 0, 0, 0))
ax[-1, 0].set_ylabel('.', color=(0, 0, 0, 0))
# Make common axis labels
fig.text(0.5, 0.04, 'common X', va='center', ha='center', fontsize=rcParams['axes.labelsize'])
fig.text(0.04, 0.5, 'common Y', va='center', ha='center', rotation='vertical', fontsize=rcParams['axes.labelsize'])

inserisci qui la descrizione dell'immagine inserisci qui la descrizione dell'immagine


1
Bel uso dell'etichetta invisibile! Grazie
colelemonz,

3

Mi sono imbattuto in un problema simile mentre tracciavo una griglia di grafici. I grafici erano composti da due parti (superiore e inferiore). L'etichetta y doveva essere centrata su entrambe le parti.

Non volevo usare una soluzione che dipenda dalla conoscenza della posizione nella figura esterna (come fig.text ()), quindi ho manipolato la posizione y della funzione set_ylabel (). Di solito è 0,5, al centro della trama a cui viene aggiunto. Dato che il riempimento tra le parti (spazio) nel mio codice era zero, ho potuto calcolare la metà delle due parti rispetto alla parte superiore.

import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec

# Create outer and inner grid
outerGrid = gridspec.GridSpec(2, 3, width_ratios=[1,1,1], height_ratios=[1,1])
somePlot = gridspec.GridSpecFromSubplotSpec(2, 1,
               subplot_spec=outerGrid[3], height_ratios=[1,3], hspace = 0)

# Add two partial plots
partA = plt.subplot(somePlot[0])
partB = plt.subplot(somePlot[1])

# No x-ticks for the upper plot
plt.setp(partA.get_xticklabels(), visible=False)

# The center is (height(top)-height(bottom))/(2*height(top))
# Simplified to 0.5 - height(bottom)/(2*height(top))
mid = 0.5-somePlot.get_height_ratios()[1]/(2.*somePlot.get_height_ratios()[0])
# Place the y-label
partA.set_ylabel('shared label', y = mid)

plt.show()

immagine

Svantaggi:

  • La distanza orizzontale dal grafico si basa sulla parte superiore, i segni di spunta inferiori potrebbero estendersi nell'etichetta.

  • La formula non tiene conto dello spazio tra le parti.

  • Genera un'eccezione quando l'altezza della parte superiore è 0.

Esiste probabilmente una soluzione generale che tiene conto dell'imbottitura tra le figure.


Ehi, ho trovato un modo per farlo molto sulla scia della tua risposta, ma potrei risolvere alcuni di questi problemi; vedi stackoverflow.com/a/44020303/4970632 (sotto)
Luke Davis

2

Aggiornare:

Questa funzione fa ora parte del pacchetto proplot matplotlib che ho recentemente rilasciato su pypi. Per impostazione predefinita, quando si creano figure, le etichette vengono "condivise" tra gli assi.


Risposta originale:

Ho scoperto un metodo più solido:

Se conosci i kwarg bottome quelli topche sono andati in GridSpecun'inizializzazione, o altrimenti conosci le posizioni dei bordi degli assi in Figurecoordinate , puoi anche specificare la posizione dell'etichetta in Figurecoordinate con un po 'di fantasia "trasformare" la magia. Per esempio:

import matplotlib.transforms as mtransforms
bottom, top = .1, .9
f, a = plt.subplots(nrows=2, ncols=1, bottom=bottom, top=top)
avepos = (bottom+top)/2
a[0].yaxis.label.set_transform(mtransforms.blended_transform_factory(
       mtransforms.IdentityTransform(), f.transFigure # specify x, y transform
       )) # changed from default blend (IdentityTransform(), a[0].transAxes)
a[0].yaxis.label.set_position((0, avepos))
a[0].set_ylabel('Hello, world!')

... e dovresti vedere che l'etichetta si adatta ancora in modo appropriato da sinistra a destra per evitare di sovrapporsi con ticklabels, proprio come normale - ma ora si adatterà per essere sempre esattamente tra i sottotrame desiderati.

Inoltre, se non lo usi nemmeno set_position, lo ylabel apparirà di default esattamente a metà della cifra . Immagino che ciò sia dovuto al fatto che quando l'etichetta viene finalmente disegnata, matplotlibusa 0,5 per il ycoordinato senza verificare se la trasformazione delle coordinate sottostante è cambiata.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.