在我使用fix, ax = plt.subplots(...)
创建许多数字的脚本中,我收到警告 RuntimeWarning:已打开超过20个数字。通过pyplot接口(matplotlib.pyplot.figure
)创建的数字将保留,直到显式关闭,并可能消耗太多内存。
但是,我不明白为什么我收到此警告,因为在使用fig.savefig(...)
保存图后,我将其删除fig.clear(); del fig
。我的代码中没有任何一点,我一次打开多个数字。尽管如此,我还是得到了关于太多公开数字的警告。这是什么意思/如何避免收到警告?
答案 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})
如果使用此选项,则不会收到该错误,这是最简单的方法。