2012-01-23 15 views
27

Czytając następujący artykuł, udało mi się umieścić legendę poza fabułą.Matplotlib savefig z legendą poza polem

Kod:

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() wyświetla poprawne działka z legendą poza nią. Ale kiedy zapiszę go jako plik z fig.savefig(), legenda jest obcinana.

Niektóre metody googlowania pokazują mi obejścia, takie jak dodanie bbox_extra_artists=[leg.legendPatch] lub bbox_extra_artists=[leg] do savefig(), ale nie zadziałały.

Jaki jest prawidłowy sposób to zrobić? Wersja Matplotlib to 0.99.3.

Dzięki.

+1

(Widzę, że jest to stary wątek, ale jest pierwszy w google). lepsze rozwiązanie dzięki włączeniu aktorów do savefig: http://stackoverflow.com/questions/10101700/moving-matplotlib-legend-outside-of-the-axis-makes-it-cutoff-by-the-figure-box – Alleo

Odpowiedz

22

Problem polega na tym, że podczas dynamicznego drukowania, matplotlib określa granice automatycznie, aby pasowały do ​​wszystkich twoich obiektów. Po zapisaniu pliku rzeczy nie są wykonywane automatycznie, należy więc określić rozmiar rysunku, a następnie obwiednię obiektu osi. Oto jak poprawić swój kod:

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

Dziękuję , zadziałało. Mam nadzieję, że przyszłe wersje savefig() będą obsługiwały obliczanie granicy podobnie jak pyplot.show(). – niboshi

+1

możesz dodać go jako żądanie funkcji, a BTW używasz bardzo starej wersji ... – Oz123

+0

Mmm, masz rację. Rozważę uaktualnienie go do nowszego. – niboshi

10

Chociaż ta metoda działa z legendą, to nie wydaje się być dobrze współpracuje z figlegend gdy istnieje wiele wątków i chcemy pojedynczą ogólną legendę. figlegend nadal przycina się, gdy savefig. Właśnie wkleiłem moje tymczasowe rozwiązanie poniżej, na wypadek, gdyby ktoś stanął przed takim przypadkiem.

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

Dzięki za to. Czy przesłałeś do tego raport o błędzie (bbox_extra_artists?)? Mam taki sam problem jak ty, z figurą wieloosiową i figegendą poza osią. Nie mogę zastosować obejścia w mojej sytuacji. – CPBL

+0

Nie wysłałem żadnego zgłoszenia błędu. Nie jestem pewien, czy to błąd, czy jest zaprojektowany w ten sposób. – Ning

0

Jeśli wszystko inne zawiedzie, używam funkcji obwiedni Inkscape do czynienia z tym, co nazwałbym uporczywe błędy w produkcji matplotlib użytkownika. Jeśli używasz systemu GNU/Linux, po prostu zapisz to, co Matplotlib daje jako plik pdf, a następnie wyślij go do następującego

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} 
Powiązane problemy