如何删除Matplotlib图中的线条

Dav*_*ton 71 python plot matplotlib

如何以这样的方式删除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.
Run Code Online (Sandbox Code Playgroud)

那么有没有办法从轴中删除一行并获取内存? 这种潜在的解决方案也行不通.

Vor*_*ity 57

这是我为我的同事打字的一个很长的解释.我认为这也有帮助.但要耐心等待.我到达了你最终的真正问题.就像一个预告片一样,这是一个对你的Line2D对象进行额外引用的问题.

警告:在我们深入研究之前还有另一个注意事项.如果您使用IPython来测试它,那么IPython会保留自己的引用,而不是所有这些都是弱参照.因此,在IPython中测试垃圾收集不起作用.它只会让事情变得混乱.

好的,我们走了.每个matplotlib对象(Figure,Axes等)通过各种属性提供对其子艺术家的访问.以下示例变得很长,但应该是有启发性的.

我们首先创建一个Figure对象,然后Axes在该图中添加一个对象.需要注意的是axfig.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
Run Code Online (Sandbox Code Playgroud)

这也扩展到轴对象中的线:

>>> #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)
Run Code Online (Sandbox Code Playgroud)

如果您plt.show()使用上面所做的操作进行调用,您会看到一个包含一组轴和一行的图形:

包含一组轴和一条线的图形

现在,虽然我们已经看到了lines和的内容ax.lines相同,但是要注意lines变量引用的对象与被尊重的对象不同,这一点非常重要,ax.lines如下所示:

>>> id(lines), id(ax.lines)
(212754584, 211335288)
Run Code Online (Sandbox Code Playgroud)

因此,删除元素lines不会对当前绘图执行任何操作,但删除元素ax.lines会从当前绘图中删除该行.所以:

>>> #THIS DOES NOTHING:
>>> lines.pop(0)

>>> #THIS REMOVES THE FIRST LINE:
>>> ax.lines.pop(0)
Run Code Online (Sandbox Code Playgroud)

因此,如果您要运行第二行代码,则会删除当前绘图中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>]
Run Code Online (Sandbox Code Playgroud)

包含一组轴和两条线的图形

>>> #Remove that new line
>>> ax.lines.remove(lines[0])
>>> ax.lines
[<matplotlib.lines.Line2D object at 0xce84dx3>]
Run Code Online (Sandbox Code Playgroud)

包含一组轴且仅包含第二行的图形

所有上述工作都适用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>
Run Code Online (Sandbox Code Playgroud)

参考仍然有效!为什么?这是因为还有另一个引用指向该引用的Line2D对象wr.还记得怎么lines没有相同的ID,ax.lines但包含相同的元素?嗯,这就是问题所在.

>>> #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>
Run Code Online (Sandbox Code Playgroud)

所以,故事的寓意是,在你自己之后清理.如果您希望收集垃圾,但事实并非如此,那么您可能会在某个地方留下引用.

  • 我认为在当前版本的mpl中不再需要这个.艺术家有一个`remove()`函数,它会将它们从事物的mpl方面清除掉,然后你只需要跟踪你的引用. (3认同)
  • 正是我需要的.我正在绘制数千张地图,每张地图都有一张散点图,位于世界地图投影的顶部.他们每人花3秒钟!通过重新使用已绘制的地图并从ax.collections弹出结果集合,我得到了1/3秒.谢谢! (2认同)
  • 嗯,知道这个变化是什么版本的 matplotlib 吗? (2认同)

Pau*_*aul 56

我示出色的组合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)
Run Code Online (Sandbox Code Playgroud)

我检查了您的大型数据集,并且系统监视器上也确认了内存的释放.

当然,更简单的方法(当没有故障排除时)将从列表中弹出它并调用remove线对象而不创建对它的硬引用:

lines.pop(0).remove()
Run Code Online (Sandbox Code Playgroud)

  • @David Morton 我刚刚降级到 0.99.1,现在我重现了您的问题。我想我只能建议升级到 1.0.1。自 0.99.x 以来有很多*很多*的错误修正 (2认同)

小智 11

我在不同的论坛上尝试了很多不同的答案.我想这取决于你开发的机器.但我已经使用了这个声明

ax.lines = []
Run Code Online (Sandbox Code Playgroud)

并且工作得很好.我不使用cla()因为它删除了我对绘图所做的所有定义

防爆.

pylab.setp(_self.ax.get_yticklabels(), fontsize=8)
Run Code Online (Sandbox Code Playgroud)

但我已经尝试多次删除这些行.我还在删除时使用weakref库来检查对该行的引用,但对我来说没有任何作用.

希望这适用于其他人= D.


小智 10

希望这可以帮助其他人:上面的示例使用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
Run Code Online (Sandbox Code Playgroud)

  • 这应该在列表中更靠前,因为其他答案确实已经过时且令人困惑。 (3认同)

小智 5

(使用与上述人员相同的示例)

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()
Run Code Online (Sandbox Code Playgroud)