Matplotlib savefig avec une légende en dehors de l'intrigue

en lisant l'article suivant, j'ai réussi à mettre une légende à l'extérieur de l'intrigue.

code:

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() affiche le tracé correct avec une légende à l'extérieur. Mais quand je l'enregistre comme un fichier avec fig.savefig() , la légende est tronquée.

certains googling me montre des solutions de travail telles que l'ajout bbox_extra_artists=[leg.legendPatch] ou bbox_extra_artists=[leg] à savefig() , mais non ouvrés.

Quelle est la bonne façon de le faire? La version Matplotlib est 0.99.3.

Merci.

29
demandé sur Community 2012-01-23 16:46:21

3 réponses

le problème est que lorsque vous tracez dynamiquement, matplotlib détermine automatiquement les frontières pour s'adapter à tous vos objets. Lorsque vous enregistrez un fichier, les choses ne sont pas fait automatiquement, donc vous devez spécifier la taille de votre figure, et puis la boîte englobante de vos axes de l'objet. Voici comment corriger votre code:

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')
23
répondu Oz123 2016-12-18 16:18:29

bien que cette méthode fonctionne avec la légende, il semble ne pas fonctionner bien avec figlegend quand il ya plusieurs sous-lots et nous voulons une légende globale unique. figlegend se fait toujours recadrer à savefig. Je viens de coller ma solution temporaire ci-dessous au cas où quelqu'un ferait face à une telle affaire.

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])
10
répondu Ning 2013-08-12 04:24:59

si tout le reste échoue, j'utilise les fonctionnalités de bounding-box D'Inkscape pour gérer ce que j'appellerais des bogues persistants dans la sortie de matplotlib. Si vous utilisez GNU/Linux, enregistrez simplement tout ce que Matplotlib vous donne en format pdf, puis envoyez-le à l'adresse suivante

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}
0
répondu CPBL 2014-07-23 08:53:46