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

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


当前回答

这里有更多的细节来扩展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
       
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})

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

这里有更多的细节来扩展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%)。

下面的代码片段为我解决了这个问题:


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  
plt.rcParams.update({'figure.max_open_warning': 0})

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