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

然而,我不明白为什么我会得到这个警告,因为在用fig.savefig(…)保存图形后,我用fig.clear()删除它;在我的代码中,我没有一次打开一个以上的数字。尽管如此,我还是得到了关于太多未公开数字的警告。这是什么意思/我怎样才能避免得到警告?


在您的图形对象上使用.clf或.cla,而不是创建一个新的图形。从@DavidZwicker

假设您已经导入pyplot为

import matplotlib.pyplot as plt

Plt.cla()清除一个轴,即当前图中的当前活动轴。它不影响其他轴。

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

Plt.close()关闭一个窗口,如果没有指定的话,该窗口将是当前窗口。Plt.close ('all')将关闭所有打开的数字。

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

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


这里有更多的细节来扩展hook的答案。当我第一次阅读这个答案时,我错过了调用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()

我测量了性能,看看是否值得在批处理中重用图形,当我在每个plot之后调用close()时,这个小示例程序的速度从41秒减慢到49秒(慢了20%)。


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

import matplotlib.pyplot as plt
plt.rcParams.update({'figure.max_open_warning': 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构造函数之后触发异常,也会发生这种情况。


如果你只是想暂时抑制警告,这也很有用:

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

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

如果使用这个,就不会得到错误,这是最简单的方法。


matplotlib by default keeps a reference of all the figures created through pyplot. If a single variable used for storing matplotlib figure (e.g "fig") is modified and rewritten without clearing the figure, all the plots are retained in RAM memory. Its important to use plt.cla() and plt.clf() instead of modifying and reusing the fig variable. If you are plotting thousands of different plots and saving them without clearing the figure, then eventually your RAM will get exhausted and program will get terminated. Clearing the axes and figures have a significant impact on memory usage if you are plotting many figures. You can monitor your RAM consumption in task manager (Windows) or in system monitor (Linux). First your RAM will get exhausted, then the OS starts consuming SWAP memory. Once both are exhausted, the program will get automatically terminated. Its better to clear figures, axes and close them if they are not required.