Matplotlib savefig 带有图外的图例

Posted

技术标签:

【中文标题】Matplotlib savefig 带有图外的图例【英文标题】:Matplotlib savefig with a legend outside the plot 【发布时间】:2012-02-16 19:32:41 【问题描述】:

阅读下面的文章,我设法把一个传说放在情节之外。

How to put the legend out of the plot

代码:

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() 显示正确的图,其外带有图例。但是当我将它保存为带有fig.savefig() 的文件时,图例会被截断。

一些谷歌搜索向我展示了解决方法,例如将 bbox_extra_artists=[leg.legendPatch]bbox_extra_artists=[leg] 添加到 savefig(),但都没有奏效。

正确的做法是什么? Matplotlib 版本为 0.99.3。

谢谢。

【问题讨论】:

(我看到这是旧线程,但它是谷歌的第一个)有一个更好的解决方案,包括演员到 savefig:***.com/questions/10101700/… 另一个答案***.com/a/44649558/805588 使用fig.savefig('aaa.png', bbox_inches='tight', bbox_inches="tight") 就像@MPa 建议在@dparker 指出的问题(***.com/questions/44642082/…)刚刚为我工作 【参考方案1】:

问题在于,当您动态绘制时,matplotlib 会自动确定边框以适合您的所有对象。 当您保存文件时,事情不会自动完成,因此您需要指定 图形的大小,然后是坐标区对象的边界框。 以下是如何更正您的代码:

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')

【讨论】:

谢谢,它成功了。我希望未来版本的 savefig() 将支持类似于 pyplot.show() 的边界计算。 嗯,你是对的。我会考虑将其升级到最新版本。 我知道 matplotlib 喜欢吹嘘一切都在用户的控制之下,但是这整个带有传说的东西实在是太好了。如果我把图例放在外面,我显然希望它仍然可见。窗口应该自行缩放以适应,而不是造成这种巨大的重新缩放麻烦。至少应该有一个默认的 True 选项来控制这种自动缩放行为。强迫用户通过大量的重新渲染来尝试以控制的名义获得正确的比例数字会起到相反的作用。【参考方案2】:

虽然这种方法适用于图例,但当有多个子图并且我们想要一个整体图例时,它似乎不适用于 figlegend。 figlegend 在 savefig 时仍然会被裁剪。我只是在下面粘贴了我的临时解决方案,以防有人遇到这种情况。

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])

【讨论】:

谢谢。您是否为此提交了错误报告(bbox_extra_artists?)?我和你有同样的问题,多轴图和轴外图。我无法将您的解决方法应用于我的情况。 我没有触发任何错误报告。我不确定这是一个错误还是它是这样设计的。【参考方案3】:

如果一切都失败了,我会使用 Inkscape 的边界框功能来处理我所说的 matplotlib 输出中的持久性错误。如果您正在运行 GNU/Linux,只需将 Matplotlib 提供的任何内容保存为 pdf,然后将其发送到以下位置

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

【讨论】:

以上是关于Matplotlib savefig 带有图外的图例的主要内容,如果未能解决你的问题,请参考以下文章

为什么你用matplotlib savefig保存下来的图像是一个空白图片?

图外的R图例未在pdf中显示

Matplotlib.savefig 忽略轴并在图像周围绘制黑色边框

python_matplotlib 输出(保存)矢量图方法;画图时图例说明(legend)放到图像外侧;Python_matplotlib图例放在外侧保存时显示不完整问题解决

图外的 JQPlot 图例

python中关于图例legend在图外的画法简析