关于太多公开数字的警告

时间:2014-02-19 15:00:45

标签: matplotlib

在我使用fix, ax = plt.subplots(...)创建许多数字的脚本中,我收到警告 RuntimeWarning:已打开超过20个数字。通过pyplot接口(matplotlib.pyplot.figure)创建的数字将保留,直到显式关闭,并可能消耗太多内存。

但是,我不明白为什么我收到此警告,因为在使用fig.savefig(...)保存图后,我将其删除fig.clear(); del fig。我的代码中没有任何一点,我一次打开多个数字。尽管如此,我还是得到了关于太多公开数字的警告。这是什么意思/如何避免收到警告?

6 个答案:

答案 0 :(得分:137)

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

假设您已将pyplot导入为

import matplotlib.pyplot as plt

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

plt.clf() clears the entire current figure及其所有轴,但保持窗口打开,以便可以重复用于其他绘图。

plt.close() closes a window,如果没有另外指定,它将是当前窗口。 plt.close('all')将关闭所有未结数字。

del fig不起作用的原因是pyplot状态机保持对周围数字的引用(因为它必须知道“当前数字”是什么) 。这意味着,即使您删除了您的参考图,也至少有一个实时引用,因此它永远不会被垃圾回收。

由于我在这里对集体智慧进行了轮询,因此@JoeKington在评论中提到plt.close(fig)将从pylab状态机(plt._pylab_helpers.Gcf)中删除特定的图形实例并允许它被垃圾收集。

答案 1 :(得分:17)

以下是Hooked's answer扩展的详细信息。当我第一次阅读该答案时,我错过了拨打clf() 而不是创建新图的说明。如果你去创造另一个人物,那么clf()本身并没有帮助。

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

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

为了避免警告,我必须在循环外拨打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()

如果您要批量生成图表,则可能必须同时使用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()

我测量了性能以确定是否值得重复使用批次中的数字,并且当我在每个绘图之后调用close()时,这个小样本程序从41s减慢到49s(慢20%)。 p>

答案 2 :(得分:7)

如果您打算故意在记忆中保留许多情节,但不想发出警告,您可以在生成数据之前更新您的选项。

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

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

答案 3 :(得分:1)

如果您只想暂时取消警告,这也很有用:

import matplotlib.pyplot as plt
       
with plt.rc_context(rc={'figure.max_open_warning': 0}):
    lots_of_plots()

答案 4 :(得分:0)

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


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(...
# .....

_wrapped_figure超出范围时,运行时将在内部使用__del__()来调用我们的plt.close()方法。即使在_wrapped_figure构造函数之后触发异常,也会发生这种情况。

答案 5 :(得分:0)

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

如果使用此选项,则不会收到该错误,这是最简单的方法。