Python 关于过多开放数字的警告

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/21884271/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 23:48:32  来源:igfitidea点击:

warning about too many open figures

pythonpython-3.xmatplotlib

提问by andreas-h

In a script where I create many figures with fix, ax = plt.subplots(...), I get the warning RuntimeWarning: More than 20 figures have been opened. Figures created through the pyplot interface (matplotlib.pyplot.figure) are retained until explicitly closed and may consume too much memory.

在我用 来创建许多图形的脚本中fix, ax = plt.subplots(...),我收到警告RuntimeWarning:已打开 20 多个图形。通过 pyplot 接口 ( matplotlib.pyplot.figure)创建的图会保留直到明确关闭,并且可能会消耗太多内存。

However, I don't understand whyI get this warning, because after saving the figure with fig.savefig(...), I delete it with fig.clear(); del fig. At no point in my code, I have more than one figure open at a time. Still, I get the warning about too many open figures. What does that mean / how can I avoid getting the warning?

但是,我不明白为什么会收到此警告,因为在使用 保存图形后fig.savefig(...),我使用fig.clear(); del fig. 在我的代码中,我一次打开多个图形。尽管如此,我还是收到了关于太多开放数字的警告。这是什么意思/我怎样才能避免收到警告?

采纳答案by Hooked

Use .clfor .claon your figure object instead of creating a newfigure. From @DavidZwicker

在图形对象上使用.clf.cla,而不是创建图形。来自@DavidZwicker

Assuming you have imported pyplotas

假设您已导入pyplot

import matplotlib.pyplot as plt

plt.cla()clears an axis, i.e. the currently active axis in the current figure. It leaves the other axes untouched.

plt.cla()清除轴,即当前图形中的当前活动轴。它使其他轴保持不变。

plt.clf()clears the entire current figurewith all its axes, but leaves the window opened, such that it may be reused for other plots.

plt.clf()清除整个当前图形及其所有轴,但保持窗口打开,以便它可以重新用于其他绘图。

plt.close()closes a window, which will be the current window, if not specified otherwise. plt.close('all')will close all open figures.

plt.close()关闭一个 window,如果没有另外指定,它将是当前窗口。plt.close('all')将关闭所有打开的数字。

The reason that del figdoes not work is that the pyplotstate-machine keeps a reference to the figure around (as it must if it is going to know what the 'current figure' is). This means that even if you delete yourref to the figure, there is at least one live ref, hence it will never be garbage collected.

del fig不起作用的原因是pyplot状态机保留对周围图形的引用(如果它要知道“当前图形”是什么,则必须这样做)。这意味着即使您删除了对图形引用,也至少有一个活引用,因此它永远不会被垃圾收集。

Since I'm polling on the collective wisdom here for this answer, @JoeKington mentions in the comments that plt.close(fig)will remove a specific figure instance from the pylab state machine (plt._pylab_helpers.Gcf) and allow it to be garbage collected.

由于我在这里为这个答案投票集体智慧,@JoeKington 在评论中提到plt.close(fig)将从 pylab 状态机(plt._pylab_helpers.Gcf)中删除特定的图形实例并允许它被垃圾收集。

回答by Don Kirkby

Here's a bit more detail to expand on Hooked's answer. When I first read that answer, I missed the instruction to call clf()instead of creating a new figure. clf()on its own doesn't help if you then go and create another figure.

这里有更多细节可以扩展Hooked 的答案。当我第一次阅读该答案时,我错过了调用clf()而不是创建新图形的说明clf()如果你再去创造另一个人物,它本身并没有帮助。

Here's a trivial example that causes the warning:

这是导致警告的一个简单示例:

from matplotlib import pyplot as plt, patches
import os


def main():
    path = 'figures'
    for i in range(21):
        _fig, ax = plt.subplots()
        x = range(3*i)
        y = [n*n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.clf()
    print('Done.')

main()

To avoid the warning, I have to pull the call to subplots()outside the loop. In order to keep seeing the rectangles, I need to switch clf()to cla(). That clears the axis without removing the axis itself.

为了避免警告,我必须将呼叫拉到subplots()循环之外。为了继续看到矩形,我需要切换clf()cla(). 这会清除轴而不移除轴本身。

from matplotlib import pyplot as plt, patches
import os


def main():
    path = 'figures'
    _fig, ax = plt.subplots()
    for i in range(21):
        x = range(3*i)
        y = [n*n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.cla()
    print('Done.')

main()

If you're generating plots in batches, you might have to use both cla()and close(). I ran into a problem where a batch could have more than 20 plots without complaining, but it would complain after 20 batches. I fixed that by using cla()after each plot, and close()after each batch.

如果您批量生成绘图,则可能必须同时使用cla()close()。我遇到了一个问题,一个批次可以有 20 多个地块而不会抱怨,但它会在 20 个批次后抱怨。我通过cla()在每个情节之后和close()每批之后使用来解决这个问题。

from matplotlib import pyplot as plt, patches
import os


def main():
    for i in range(21):
        print('Batch {}'.format(i))
        make_plots('figures')
    print('Done.')


def make_plots(path):
    fig, ax = plt.subplots()
    for i in range(21):
        x = range(3 * i)
        y = [n * n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.cla()
    plt.close(fig)


main()

I measured the performance to see if it was worth reusing the figure within a batch, and this little sample program slowed from 41s to 49s (20% slower) when I just called close()after every plot.

我测量了性能,看看是否值得在一个批次中重用这个数字,当我close()在每次绘图后调用时,这个小示例程序从 41 秒减慢到 49 秒(慢 20%)。

回答by mightypile

If you intend to knowingly keep many plots in memory, but don't want to be warned about it, you can update your options prior to generating figures.

如果您打算有意在内存中保留许多绘图,但又不想收到警告,则可以在生成数字之前更新您的选项。

import matplotlib.pyplot as plt
plt.rcParams.update({'figure.max_open_warning': 0})

This will prevent the warning from being emitted without changing anything about the way memory is managed.

这将防止在不改变内存管理方式的情况下发出警告。

回答by Dmitry

The following snippet solved the issue for me:

以下代码段为我解决了这个问题:


class FigureWrapper(object):
    '''Frees underlying figure when it goes out of scope. 
    '''

    def __init__(self, figure):
        self._figure = figure

    def __del__(self):
        plt.close(self._figure)
        print("Figure removed")


# .....
    f, ax = plt.subplots(1, figsize=(20, 20))
    _wrapped_figure = FigureWrapper(f)

    ax.plot(...
    plt.savefig(...
# .....

When _wrapped_figuregoes out of scope the runtime calls our __del__()method with plt.close()inside. It happens even if exception fires after _wrapped_figureconstructor.

_wrapped_figure超出范围时,运行时会__del__()使用plt.close()inside调用我们的方法。即使在_wrapped_figure构造函数之后触发异常,它也会发生。