如何以实际获取垃圾的方式删除matplotlib轴的一行(或多行)并释放内存?下面的代码似乎删除了行,但从不释放内存(即使显式调用gc.collect())
from matplotlib import pyplot
import numpy
a = numpy.arange(int(1e7))
# large so you can easily see the memory footprint on the system monitor.
fig = pyplot.Figure()
ax = pyplot.add_subplot(1, 1, 1)
lines = ax.plot(a) # this uses up an additional 230 Mb of memory.
# can I get the memory back?
l = lines[0]
l.remove()
del l
del lines
# not releasing memory
ax.cla() # this does release the memory, but also wipes out all other lines.
那么有没有办法从轴中删除一行并获取内存? This potential solution也不起作用。
答案 0 :(得分:53)
这是我为我的同事打字的一个很长的解释。我认为这也有帮助。但要耐心等待。我到达了你最终的真正问题。就像预告片一样,这是一个额外引用你的Line2D
对象的问题。
警告:在我们潜入之前另一个注意事项。如果你使用IPython测试它,IPython会保留自己的引用,而不是所有这些都是weakrefs。因此,在IPython中测试垃圾收集不起作用。这只会让事情变得混乱。
好的,我们走了。每个matplotlib
对象(Figure
,Axes
等)都可以通过各种属性访问其子艺术家。下面的例子很长,但应该很有启发性。
我们首先创建一个Figure
对象,然后在该图中添加一个Axes
对象。请注意,ax
和fig.axes[0]
是同一个对象(同一个id()
)。
>>> #Create a figure
>>> fig = plt.figure()
>>> fig.axes
[]
>>> #Add an axes object
>>> ax = fig.add_subplot(1,1,1)
>>> #The object in ax is the same as the object in fig.axes[0], which is
>>> # a list of axes objects attached to fig
>>> print ax
Axes(0.125,0.1;0.775x0.8)
>>> print fig.axes[0]
Axes(0.125,0.1;0.775x0.8) #Same as "print ax"
>>> id(ax), id(fig.axes[0])
(212603664, 212603664) #Same ids => same objects
这也扩展到了轴对象中的线:
>>> #Add a line to ax
>>> lines = ax.plot(np.arange(1000))
>>> #Lines and ax.lines contain the same line2D instances
>>> print lines
[<matplotlib.lines.Line2D object at 0xce84bd0>]
>>> print ax.lines
[<matplotlib.lines.Line2D object at 0xce84bd0>]
>>> print lines[0]
Line2D(_line0)
>>> print ax.lines[0]
Line2D(_line0)
>>> #Same ID => same object
>>> id(lines[0]), id(ax.lines[0])
(216550352, 216550352)
如果您使用上面所做的操作致电plt.show()
,您会看到一个包含一组轴和一行的图形:
现在,虽然我们已经看到lines
和ax.lines
的内容相同,但请务必注意lines
变量引用的对象不一样作为ax.lines
所崇敬的对象,可以通过以下方式看出:
>>> id(lines), id(ax.lines)
(212754584, 211335288)
因此,从lines
中删除元素对当前绘图没有任何作用,但从ax.lines
中删除元素会从当前绘图中删除该行。所以:
>>> #THIS DOES NOTHING:
>>> lines.pop(0)
>>> #THIS REMOVES THE FIRST LINE:
>>> ax.lines.pop(0)
因此,如果您要运行第二行代码,则会从当前绘图中删除Line2D
中包含的ax.lines[0]
对象,它将会消失。请注意,这也可以通过ax.lines.remove()
完成,这意味着您可以将Line2D
实例保存在变量中,然后将其传递给ax.lines.remove()
以删除该行,如下所示:
>>> #Create a new line
>>> lines.append(ax.plot(np.arange(1000)/2.0))
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84bd0>, <matplotlib.lines.Line2D object at 0xce84dx3>]
>>> #Remove that new line
>>> ax.lines.remove(lines[0])
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84dx3>]
上述所有内容适用于fig.axes
,适用于ax.lines
现在,真正的问题在这里。如果我们将ax.lines[0]
中包含的引用存储到weakref.ref
对象中,然后尝试删除它,我们会注意到它没有被垃圾收集:
>>> #Create weak reference to Line2D object
>>> from weakref import ref
>>> wr = ref(ax.lines[0])
>>> print wr
<weakref at 0xb758af8; to 'Line2D' at 0xb757fd0>
>>> print wr()
<matplotlib.lines.Line2D at 0xb757fd0>
>>> #Delete the line from the axes
>>> ax.lines.remove(wr())
>>> ax.lines
[]
>>> #Test weakref again
>>> print wr
<weakref at 0xb758af8; to 'Line2D' at 0xb757fd0>
>>> print wr()
<matplotlib.lines.Line2D at 0xb757fd0>
参考仍然有效!为什么?这是因为Line2D
中的引用仍指向wr
对象的另一个引用。还记得lines
如何与ax.lines
具有相同的ID但包含相同的元素吗?嗯,这就是问题所在。
>>> #Print out lines
>>> print lines
[<matplotlib.lines.Line2D object at 0xce84bd0>, <matplotlib.lines.Line2D object at 0xce84dx3>]
To fix this problem, we simply need to delete `lines`, empty it, or let it go out of scope.
>>> #Reinitialize lines to empty list
>>> lines = []
>>> print lines
[]
>>> print wr
<weakref at 0xb758af8; dead>
所以,故事的寓意是,在你自己之后清理。如果你希望垃圾被收集,但事实并非如此,你可能会在某个地方留下一个引用。
答案 1 :(得分:48)
我发现lines.pop(0)
l.remove()
和del l
的组合可以解决问题。
from matplotlib import pyplot
import numpy, weakref
a = numpy.arange(int(1e3))
fig = pyplot.Figure()
ax = fig.add_subplot(1, 1, 1)
lines = ax.plot(a)
l = lines.pop(0)
wl = weakref.ref(l) # create a weak reference to see if references still exist
# to this object
print wl # not dead
l.remove()
print wl # not dead
del l
print wl # dead (remove either of the steps above and this is still live)
我检查了您的大型数据集,并且系统监视器上也确认了内存的释放。
当然,更简单的方法(在没有故障排除时)将从列表中弹出它并在线对象上调用remove
而不创建对它的硬引用:
lines.pop(0).remove()
答案 2 :(得分:10)
我在不同的论坛上尝试了很多不同的答案。我想这取决于你开发的机器。但我已经使用了声明
ax.lines = []
并且完美无缺。我不使用cla()因为它删除了我对绘图的所有定义
实施例
pylab.setp(_self.ax.get_yticklabels(), fontsize=8)
但我尝试多次删除这些行。还在我删除时使用weakref库检查对该行的引用,但对我没有任何作用。
希望这适用于其他人= D
答案 3 :(得分:4)
(使用与上面相同的例子)
from matplotlib import pyplot
import numpy
a = numpy.arange(int(1e3))
fig = pyplot.Figure()
ax = fig.add_subplot(1, 1, 1)
lines = ax.plot(a)
for i, line in enumerate(ax.lines):
ax.lines.pop(i)
line.remove()
答案 4 :(得分:1)
希望这可以对其他人有所帮助:上面的示例使用ax.lines
。
在最新的mpl(3.3.1)中,有ax.get_lines()
。
这样就无需调用ax.lines=[]
for line in ax.get_lines(): # ax.lines:
line.remove()
# ax.lines=[] # needed to complete removal when using ax.lines