2012-01-23 47 views
27

Leggendo il seguente articolo, sono riuscito a mettere una legenda al di fuori della trama.Salvataggio di Matplotlib con una legenda all'esterno della trama

codice:

import matplotlib.pyplot as pyplot 

x = [0, 1, 2, 3, 4] 
y = [xx*xx for xx in x] 

fig = pyplot.figure() 
ax = fig.add_subplot(111) 

box = ax.get_position() 
ax.set_position([box.x0, box.y0, box.width*0.8, box.height]) 

ax.plot(x, y) 
leg = ax.legend(['abc'], loc = 'center left', bbox_to_anchor = (1.0, 0.5)) 
#pyplot.show() 

fig.savefig('aaa.png', bbox_inches='tight') 

pyplot.show() mostra il grafico corretto con una legenda di fuori di essa. Ma quando lo salvo come file con fig.savefig(), la legenda viene troncata.

Alcuni googling mi mostra soluzioni alternative come l'aggiunta di bbox_extra_artists=[leg.legendPatch] o bbox_extra_artists=[leg] a savefig(), ma nessuno dei due ha funzionato.

Qual è il modo corretto per farlo? La versione Matplotlib è 0.99.3.

Grazie.

+1

(vedo questo è vecchio thread, ma è prima in google) V'è una soluzione migliore includendo gli attori di savefig: http://stackoverflow.com/questions/10101700/moving-matplotlib-legend-outside-of-the-axis-makes-it-cutoff-by-the-figure-box – Alleo

risposta

22

Il problema è che quando si stampa dinamicamente, matplotlib determina automaticamente i bordi per adattarsi a tutti gli oggetti. Quando si salva un file, le cose non vengono eseguite automaticamente, quindi è necessario specificare le dimensioni della propria figura e quindi il rettangolo di selezione dell'oggetto assi. Ecco come correggere il codice:

import matplotlib.pyplot as pyplot 

x = [0, 1, 2, 3, 4] 
y = [xx*xx for xx in x] 

fig = pyplot.figure(figsize=(3,3)) 
ax = fig.add_subplot(111) 

#box = ax.get_position() 
#ax.set_position([0.3, 0.4, box.width*0.3, box.height]) 
# you can set the position manually, with setting left,buttom, witdh, hight of the axis 
# object 
ax.set_position([0.1,0.1,0.5,0.8]) 
ax.plot(x, y) 
leg = ax.legend(['abc'], loc = 'center left', bbox_to_anchor = (1.0, 0.5)) 

fig.savefig('aaa.png') 
+0

Grazie , ha funzionato. Spero che le versioni future di savefig() supporteranno il calcolo del bordo simile a pyplot.show(). – niboshi

+1

puoi aggiungerlo come richiesta di funzionalità e BTW stai utilizzando una versione molto vecchia ... – Oz123

+0

Mmm, hai ragione. Prenderò in considerazione l'aggiornamento a quello più recente. – niboshi

10

Anche se questo metodo funziona con la leggenda, sembra non funzionare bene con figlegend quando ci sono più sottotrame e vogliamo un unico leggenda complessiva. figlegend viene ancora ritagliato quando savefig. Ho appena incollato la mia soluzione temporanea di seguito nel caso qualcuno dovesse affrontare un caso del genere.

import matplotlib.pyplot as plt 

para = { 
    ## this parameter will indicate the position of 
    ## subplot within figure, but will not be shown 
    ## if using bbox_inches='tight' when saving 
    'figure.subplot.top': 0.5 
} 
#plt.rcParams.update(para) 

fig = plt.figure() 

ax=fig.add_subplot(221) 
## only needed when what to manually control 
## subplot ration 
#ax.set_position([0.1,0.6,0.5, 0.4]) 
ax.plot([1,1,1]) 


ax=fig.add_subplot(222) 
#ax.set_position([0.7,0.6,0.5, 0.4]) 
ax.plot([2,2,2]) 

ax=fig.add_subplot(223) 
#ax.set_position([0.1,0.1,0.5, 0.4]) 
ax.plot([3,3,3]) 


ax=fig.add_subplot(224) 
#ax.set_position([0.7,0.1,0.5, 0.4]) 
p1, = ax.plot([4,4,4]) 
p2, = ax.plot([2,3,2]) 

## figlegend does not work fine with tight bbox 
## the legend always get cropped by this option 
## even add bbox extra will not help 
## had to use legend, and manually adjust it to 
## arbitary position such as (0.3, 2.5) 

## http://matplotlib.org/users/tight_layout_guide.html 
## according to this link, tight layout is only 
## an experimental feature, might not support figlegend 

#lgd = plt.figlend(
lgd = plt.legend(
    [p1,p2], 
    ['a', 'b'], 
    ## by default, legend anchor to axis, but can 
    ## also be anchored to arbitary position 
    ## positions within [1,1] would be within the figure 
    ## all numbers are ratio by default 

    bbox_to_anchor=(-0.1, 2.5), 

    ## loc indicates the position within the figure 
    ## it is defined consistent to the same Matlab function 
    loc='center', 

    ncol=2 
    #mode="expand", 
    #borderaxespad=0. 
    ) 



#plt.show() 

plt.savefig('temp.png', bbox_inches='tight')#, bbox_extra_artist=[lgd]) 
+0

Grazie per questo. Hai inviato una segnalazione di bug per questo (bbox_extra_artists?)? Ho lo stesso problema di te, con una figura multiasse e una figlegend fuori dall'asse. Non riesco ad applicare la soluzione alternativa alla mia situazione. – CPBL

+0

Non ho segnalato alcun bug report. Non sono sicuro che questo sia un bug o che sia progettato in questo modo. – Ning

0

Se tutto il resto fallisce, io uso le caratteristiche di delimitazione-box di Inkscape a che fare con quello che chiamerei bug persistenti in uscita di matplotlib. Se stai usando GNU/Linux, basta salvare qualunque Matplotlib ti dà in formato pdf, e poi inviarlo al seguente

def tightBoundingBoxInkscape(pdffile,use_xvfb=True): 
    """Makes POSIX-specific OS calls. Preferably, have xvfb installed, to avoid any GUI popping up in the background. If it fails anyway, could always resort to use_xvfb=False, which will allow some GUIs to show as they carry out the task 
     pdffile: the path for a PDF file, without its extension 
    """ 
    usexvfb='xvfb-run '*use_xvfb 
    import os 
    assert not pdffile.endswith('.pdf') 
    os.system(""" 
     inkscape -f %(FN)s.pdf -l %(FN)s_tmp.svg 
     inkscape -f %(FN)s_tmp.svg --verb=FitCanvasToDrawing \ 
            --verb=FileSave \ 
            --verb=FileQuit 
     inkscape -f %(FN)s_tmp.svg -A %(FN)s-tightbb.pdf 
"""%{'FN':pdffile} 
Problemi correlati