2013-10-09 6 views
16

Nel grafico seguente sono presenti due grafici a dispersione con scala numerica diversa, quindi le etichette dell'asse Y non sono allineate. C'è un modo per forzare l'allineamento orizzontale nelle etichette dell'asse y?matplotlib: allineamento delle etichette dell'asse y nei grafici a dispersione sovrapposti

import matplotlib.pylab as plt 
import random 
import matplotlib.gridspec as gridspec 

random.seed(20) 
data1 = [random.random() for i in range(10)] 
data2 = [random.random()*1000 for i in range(10)] 

gs = gridspec.GridSpec(2,1) 
fig = plt.figure() 

ax = fig.add_subplot(gs[0]) 
ax.plot(data1) 
ax.set_ylabel(r'Label One', size =16) 

ax = fig.add_subplot(gs[1]) 
ax.plot(data2) 
ax.set_ylabel(r'Label Two', size =16) 

plt.show() 

stacked scatter plots

+0

quello che vuoi è descritto qui: http://matplotlib.org/faq/howto_faq.html # align-my-ylabels-across-multiple-subplots –

risposta

18

È possibile utilizzare il metodo set_label_coords.

import matplotlib.pylab as plt 
import random 
import matplotlib.gridspec as gridspec 

random.seed(20) 
data1 = [random.random() for i in range(10)] 
data2 = [random.random()*1000 for i in range(10)] 

gs = gridspec.GridSpec(2,1) 
fig = plt.figure() 

ax = fig.add_subplot(gs[0]) 
ax.plot(data1) 
ax.set_ylabel(r'Label One', size =16) 
ax.get_yaxis().set_label_coords(-0.1,0.5) 

ax = fig.add_subplot(gs[1]) 
ax.plot(data2) 
ax.set_ylabel(r'Label Two', size =16) 
ax.get_yaxis().set_label_coords(-0.1,0.5) 

enter image description here

+0

grazie! Stavo cercando di fare qualcosa sulla falsariga di "ax.get_yaxis(). Get_label(). Set_ha ('left')" ma non ha funzionato. Immagino di non aver capito completamente come funzionano gli allineamenti delle etichette. – dimka

+3

C'è un modo per * automaticamente * determinare quale è la distanza minima accettabile dalle etichette di graduazione su entrambi gli assi? Cioè per trovare quali sarebbero le posizioni automatiche indipendenti, quindi impostarle esplicitamente tutte uguali alla più grande? – andybuckley

+3

@andybuckley, ho appena giocato con l'allineamento automatico delle etichette. Puoi ottenere i valori min/max delle posizioni usando 'ax.yaxis.label.get_position()', ma lo spazio delle coordinate non è lo stesso di quello usato in 'ax.yaxis.set_label_coords()' - hai per usare 'ax.yaxis.label.set_position()' e un po 'di hack: 'ax.yaxis._autolabelpos = False' oppure resetta la posizione quando chiami' draw() ' –

2

Come ha pubblicato un commento, quello che stai cercando è risolto utilizzando set_label_coords() come described here. Per il vostro caso sarà qualcosa del tipo:

labelx = -0.5 

ax = fig.add_subplot(gs[0]) 
ax.plot(data1) 
ax.set_ylabel(r'Label One', size=16) 
ax.yaxis.set_label_coords(labelx, 0.5) 

ax = fig.add_subplot(gs[1]) 
ax.plot(data2) 
ax.set_ylabel(r'Label Two', size=16) 
ax.yaxis.set_label_coords(labelx, 0.5) 
1

Ecco una funzione che ho scritto per allineare automaticamente le etichette, ma non sembra funzionare in uno script, ma solo in modo interattivo.

def align_labels(axes_list,axis='y',align=None): 
    if align is None: 
     align = 'l' if axis == 'y' else 'b' 
    yx,xy = [],[] 
    for ax in axes_list: 
     yx.append(ax.yaxis.label.get_position()[0]) 
     xy.append(ax.xaxis.label.get_position()[1]) 

    if axis == 'x': 
     if align in ('t','top'): 
      lim = max(xy) 
     elif align in ('b','bottom'): 
      lim = min(xy) 
    else: 
     if align in ('l','left'): 
      lim = min(yx) 
     elif align in ('r','right'): 
      lim = max(yx) 

    if align in ('t','b','top','bottom'): 
     for ax in axes_list: 
      t = ax.xaxis.label.get_transform() 
      x,y = ax.xaxis.label.get_position() 
      ax.xaxis.set_label_coords(x,lim,t) 
    else: 
     for ax in axes_list: 
      t = ax.yaxis.label.get_transform() 
      x,y = ax.yaxis.label.get_position() 
      ax.yaxis.set_label_coords(lim,y,t) 

E un esempio:

fig,ax = subplots(2,2) 
ax00,ax01 = ax[0] 
ax10,ax11 = ax[1] 
ax00.set_ylim(1000,5000) 
ax00.set_ylabel('top') 
ax10.set_ylabel('bottom') 
ax10.set_xlabel('left') 
ax11.set_xlabel('right') 
ax11.xaxis.axis_date() 
fig.autofmt_xdate() 
#we have to call draw() so that matplotlib will figure out the automatic positions 
fig.canvas.draw() 
align_labels(ax[:,0],'y') 
align_labels(ax[1],'x') 

example figure

0

fornisco una soluzione alla fine, ma prima dico in che modo non porta al successo.

ho rivisitato questo problema di recente, e trascorso molto tempo con provare varie soluzioni, cioè cercando quasi tutte le possibili combinazioni di trasformazioni tra i diversi sistemi di coordinate e la loro relazione con la tight_layout(). Ho sperimentato solo con backend_pdf, quindi non posso dire di media interattivi. Ma in breve, la mia conclusione è che non importa come tu cerchi di scoprire le posizioni e provare a trasformarle, non è possibile a questo livello allineare le etichette degli assi. Immagino che in qualche modo dovrebbe essere possibile, per esempio in qualche modo internamente matplotlib è in grado di allineare gli assi delle sottotrame stesse. Solo con il disegno nel file pdf due volte e fare qualcosa di simile sotto inbetween, ho potuto raggiungere posizioni migliori, ma ancora non allineato:

# sorry for the `self`, this is from a class 
def align_x_labels(self): 
    self.lowest_ax = min(self.axes.values(), 
         key = lambda ax: ax.xaxis.label.get_position()[1]) 
    self.lowest_xlab_dcoo = self.lowest_ax.transData.transform(
     self.lowest_ax.xaxis.label.get_position()) 
    list(
     map(
       lambda ax: \ 
        ax.xaxis.set_label_coords(
         self.fig.transFigure.inverted().transform(
          ax.transAxes.transform((0.5, 0.5)))[0], 
         self.fig.transFigure.inverted().transform(
          self.lowest_xlab_dcoo)[1], 
         transform = self.fig.transFigure 
        ), 
       self.axes.values() 
      ) 
    ) 

Si tratta di una vergogna come una funzionalità di base non può essere raggiunto, ed è oscura il modo in cui i diversi spazi di coordinate vengono trasformati e riscalati nei diversi passaggi della trama. Gradirei molto vedere una chiara spiegazione di ciò, perché lo matplotlib webpage descrive solo l'architettura, presenta casi semplici, ma non riesce a spiegare situazioni come questa. Inoltre sono sorpreso che i metodi che accettano o restituiscono le coordinate non dicono nella loro docstring quali sono i tipi di coordinate. Finalmente ho trovato molto utile this tutorial.

Soluzione

Alla fine, invece di scherzi con trasformazioni, ho creato nel GridSpec una riga aggiuntiva di altezza zero e assi invisibili (stesso può essere fatto con una colonna larghezza zero per etichette dell'asse y). Poi ho aggiunto etichette per queste sottotrame, impostando lo verticalalignment su top.

# get one of the zero height phantom subplots to `self.ax`: 
self.get_subplot(i, 1) 
# set empty ticklabels: 
self.ax.xaxis.set_ticklabels([]) 
self.ax.yaxis.set_ticklabels([]) 
# set the axis label: 
self.ax.set_xlabel(labtext, fontproperties = self.fp_axis_lab) 
# and this is matter of aesthetics 
# sometimes bottom or center might look better: 
self.ax.xaxis.label.set_verticalalignment('top')