2012-01-23 47 views
27

Al leer el siguiente artículo, logré poner una leyenda fuera de la trama.Matplotlib savefig con una leyenda fuera de la trama

código:

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() muestra la trama correcta con una leyenda fuera de ella. Pero cuando lo guardo como un archivo con fig.savefig(), la leyenda se trunca.

Algunos google me muestran soluciones como agregar bbox_extra_artists=[leg.legendPatch] o bbox_extra_artists=[leg] a savefig(), pero ninguno de los dos funcionó.

¿Cuál es la forma correcta de hacerlo? La versión Matplotlib es 0.99.3.

Gracias.

+1

(Veo esto es viejo hilo, pero es en primer lugar en Google) Hay una mejor solución al incluir actores para guardar: http://stackoverflow.com/questions/10101700/moving-matplotlib-legend-outside-of-the-axis-makes-it-cutoff-by-the-figure-box – Alleo

Respuesta

22

El problema es que cuando traza dinámicamente, matplotlib determina los bordes automáticamente para que quepan todos sus objetos. Cuando guarda un archivo, las cosas no se están haciendo automáticamente, por lo que debe especificar el tamaño de su figura, y luego el cuadro delimitador de su objeto de ejes. Aquí es cómo corregir su código:

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

Gracias , funcionó. Espero que las versiones futuras de savefig() admitan un cálculo de borde similar a pyplot.show(). – niboshi

+1

puede agregarlo como solicitud de función y, por cierto, está utilizando una versión muy antigua ... – Oz123

+0

Mmm, tiene razón. Consideraré actualizarlo a uno más reciente. – niboshi

10

Aunque este método funciona con la leyenda, parece no estar funcionando bien con figlegend cuando hay múltiples subtramas y queremos una sola leyenda general. figlegend aún se recorta cuando savefig. Acabo de pegar mi solución temporal a continuación en caso de que alguien se enfrenta a un caso así.

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

Gracias por esto. ¿Has enviado un informe de error para esto (bbox_extra_artists?)? Tengo el mismo problema que tú, con una figura de varios ejes y un figlegend fuera de eje. No puedo aplicar su solución a mi situación. – CPBL

+0

No he disparado ningún informe de error. No estoy seguro de si esto es un error o está diseñado así. – Ning

0

Si todo lo demás falla, puedo utilizar las funciones del cuadro delimitador de Inkscape para hacer frente a lo que yo llamaría errores persistentes en la producción de matplotlib. Si está ejecutando GNU/Linux, acaba de salvar lo Matplotlib que da como pdf, y luego enviarla a la siguiente

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} 
Cuestiones relacionadas