【问题标题】:Matplotlib runs out of memory when plotting in a loopMatplotlib 在循环中绘制时内存不足
【发布时间】:2011-01-22 19:47:07
【问题描述】:

我有一个相当简单的绘图例程,如下所示:

from __future__ import division
import datetime
import matplotlib
matplotlib.use('Agg')
from matplotlib.pyplot import figure, plot, show, legend, close, savefig, rcParams
import numpy
from globalconstants import *

    def plotColumns(columnNumbers, t, out, showFig=False, filenamePrefix=None, saveFig=True, saveThumb=True):
        lineProps = ['b', 'r', 'g', 'c', 'm', 'y', 'k', 'b--', 'r--', 'g--', 'c--', 'm--', 'y--', 'k--', 'g--', 'b.-', 'r.-', 'g.-', 'c.-', 'm.-', 'y.-', 'k.-']

        rcParams['figure.figsize'] = (13,11)
        for i in columnNumbers:
            plot(t, out[:,i], lineProps[i])

        legendStrings = list(numpy.zeros(NUMCOMPONENTS)) 
        legendStrings[GLUCOSE] = 'GLUCOSE'
        legendStrings[CELLULOSE] = 'CELLULOSE'
        legendStrings[STARCH] = 'STARCH'
        legendStrings[ACETATE] = 'ACETATE'
        legendStrings[BUTYRATE] = 'BUTYRATE'
        legendStrings[SUCCINATE] = 'SUCCINATE'
        legendStrings[HYDROGEN] = 'HYDROGEN'
        legendStrings[PROPIONATE] = 'PROPIONATE'
        legendStrings[METHANE] = "METHANE"

        legendStrings[RUMINOCOCCUS] = 'RUMINOCOCCUS'
        legendStrings[METHANOBACTERIUM] = "METHANOBACTERIUM"
        legendStrings[BACTEROIDES] = 'BACTEROIDES'
        legendStrings[SELENOMONAS] = 'SELENOMONAS'
        legendStrings[CLOSTRIDIUM] = 'CLOSTRIDIUM'

        legendStrings = [legendStrings[i] for i in columnNumbers]
        legend(legendStrings, loc='best')

        dt = datetime.datetime.now()
        dtAsString = dt.strftime('%d-%m-%Y_%H-%M-%S')

        if filenamePrefix is None:
            filenamePrefix = ''

        if filenamePrefix != '' and filenamePrefix[-1] != '_':
            filenamePrefix += '_'

        if saveFig: 
            savefig(filenamePrefix+dtAsString+'.eps')

        if saveThumb:
            savefig(filenamePrefix+dtAsString+'.png', dpi=300)


        if showFig: f.show()

        close('all')

当我在单次迭代中绘制它时,它工作正常。然而,当我把它放在一个循环中的那一刻,matplotlib 抛出了一个嘶嘶声......

Traceback (most recent call last):
  File "c4hm_param_variation_h2_conc.py", line 148, in <module>
    plotColumns(columnNumbers, timeVector, out, showFig=False, filenamePrefix='c
4hm_param_variation_h2_conc_'+str(hydrogen_conc), saveFig=False, saveThumb=True)

  File "D:\phdproject\alexander paper\python\v3\plotcolumns.py", line 48, in plo
tColumns
    savefig(filenamePrefix+dtAsString+'.png', dpi=300)
  File "C:\Python25\lib\site-packages\matplotlib\pyplot.py", line 356, in savefi
g
    return fig.savefig(*args, **kwargs)
  File "C:\Python25\lib\site-packages\matplotlib\figure.py", line 1032, in savef
ig
    self.canvas.print_figure(*args, **kwargs)
  File "C:\Python25\lib\site-packages\matplotlib\backend_bases.py", line 1476, i
n print_figure
    **kwargs)
  File "C:\Python25\lib\site-packages\matplotlib\backends\backend_agg.py", line
358, in print_png
    FigureCanvasAgg.draw(self)
  File "C:\Python25\lib\site-packages\matplotlib\backends\backend_agg.py", line
314, in draw
    self.figure.draw(self.renderer)
  File "C:\Python25\lib\site-packages\matplotlib\artist.py", line 46, in draw_wr
apper
    draw(artist, renderer, *kl)
  File "C:\Python25\lib\site-packages\matplotlib\figure.py", line 773, in draw
    for a in self.axes: a.draw(renderer)
  File "C:\Python25\lib\site-packages\matplotlib\artist.py", line 46, in draw_wr
apper
    draw(artist, renderer, *kl)
  File "C:\Python25\lib\site-packages\matplotlib\axes.py", line 1735, in draw
    a.draw(renderer)
  File "C:\Python25\lib\site-packages\matplotlib\artist.py", line 46, in draw_wr
apper
    draw(artist, renderer, *kl)
  File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 374, in draw
    bbox = self._legend_box.get_window_extent(renderer)
  File "C:\Python25\lib\site-packages\matplotlib\offsetbox.py", line 209, in get
_window_extent
    px, py = self.get_offset(w, h, xd, yd)
  File "C:\Python25\lib\site-packages\matplotlib\offsetbox.py", line 162, in get
_offset
    return self._offset(width, height, xdescent, ydescent)
  File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 360, in findof
fset
    return _findoffset(width, height, xdescent, ydescent, renderer)
  File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 325, in _findo
ffset_best
    ox, oy = self._find_best_position(width, height, renderer)
  File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 817, in _find_
best_position
    verts, bboxes, lines = self._auto_legend_data()
  File "C:\Python25\lib\site-packages\matplotlib\legend.py", line 669, in _auto_
legend_data
    tpath = trans.transform_path(path)
  File "C:\Python25\lib\site-packages\matplotlib\transforms.py", line 1911, in t
ransform_path
    self._a.transform_path(path))
  File "C:\Python25\lib\site-packages\matplotlib\transforms.py", line 1122, in t
ransform_path
    return Path(self.transform(path.vertices), path.codes,
  File "C:\Python25\lib\site-packages\matplotlib\transforms.py", line 1402, in t
ransform
    return affine_transform(points, mtx)
MemoryError: Could not allocate memory for path

这发生在第 2 次迭代(从 1 开始计数),如果这有影响的话。该代码在 Windows XP 32 位上运行,带有 python 2.5 和 matplotlib 0.99.1、numpy 1.3.0 和 scipy 0.7.1。

编辑:代码现已更新,以反映崩溃实际发生在调用legend() 的事实。评论调用解决了问题,但显然,我仍然希望能够在我的图表上放置一个图例......

【问题讨论】:

标签: python numpy matplotlib


【解决方案1】:

每个循环都应该生成一个新图形吗?我没有看到你关闭它或从一个循环到另一个循环创建一个新的图形实例。

此调用将在循环结束保存当前图形后清除它:

pyplot.clf()

不过,我会重构,让您的代码更加面向对象,并在每个循环上创建一个新的图形实例:

from matplotlib import pyplot

while True:
  fig = pyplot.figure()
  ax = fig.add_subplot(111)
  ax.plot(x,y)
  ax.legend(legendStrings, loc = 'best')
  fig.savefig('himom.png')
  # etc....

【讨论】:

  • 呸!我忘记了clf() 电话...添加它修复了它。
  • 从@geographica 的回答看来clf() 清除了这个数字,但没有释放对它的所有引用,这与close(fig) 不同。清除的时候不会占多少内存,但是如果有很多,就会加起来……
  • 因此,如果我有一个大约 30k 值的实时图,并且我还有其他需要绘制的弧、ellipses.circles,那么 pyplot.clf() 究竟会做什么?对于动画,我也使用了 pyplot.pause(time_duration)。如何保留之前绘制的值,然后在获取数据时一次又一次地绘制值?
  • 对我来说,它还有助于不时手动运行 Python 的垃圾收集以摆脱内存不足错误。为此,我在代码顶部添加了import gc,然后在close() 之后添加了gc.collect()(或不时添加,具体取决于代码)。
  • 我能够通过fig.clf()plt.close(fig) 解决我的问题。否则,仅使用plt.close(fig),在 Kaggle 笔记本中绘制和保存约 8000 张图像时,我仍然 OOM 超过 10 Gb。
【解决方案2】:

我也遇到过这个错误。似乎已经解决的问题是

while True:
    fig = pyplot.figure()
    ax = fig.add_subplot(111)
    ax.plot(x,y)
    ax.legend(legendStrings, loc = 'best')
    fig.savefig('himom.png')
    #new bit here
    pylab.close(fig) #where f is the figure

现在稳定地运行我的循环,内存波动但没有持续增加

【讨论】:

  • +1 pyplot.close() 在循环中释放内存并防止 Python 崩溃。
  • 即使在内存不足的情况下,这也是必要的(例如,在生成数百个之后)。 pyplot.close('all') 是在循环中绘制多个图形时要考虑的另一种方法
【解决方案3】:

ninjasmith 的回答也对我有用 - pyplot.close() 使我的循环能够工作。

来自 pyplot 教程,Working with multiple figures and axes

您可以使用clf() 清除当前数字和当前数字 带有cla() 的轴。如果你觉得这种状态很烦人,不要 绝望,这只是一个围绕对象的薄状态包装器 面向 API,您可以使用它来代替(参见 Artist tutorial

如果您要制作一长串数字,则需要注意 还有一件事:一个图所需的内存并不完全 直到图形被close() 明确关闭。删除 所有对图的引用,和/或使用窗口管理器杀死 数字出现在屏幕上的窗口是不够的, 因为 pyplot 在调用 close() 之前一直保持内部引用。

【讨论】:

    猜你喜欢
    • 2018-02-01
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    • 1970-01-01
    • 2019-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多