Matplotlib 플롯에서 선을 제거하는 방법


84

실제로 가비지를 수집하고 메모리를 다시 해제하는 방식으로 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.

축에서 한 줄만 삭제하고 메모리를 되 찾는 방법이 있습니까? 이 잠재적 인 솔루션 도 작동하지 않습니다.

답변:


70

나는의 조합을 보여주는거야 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()

코드를 실행 한 결과 다음과 같습니다. [8:37 pm] @flattop : ~ / Desktop / sandbox> python delete_lines.py <weakref at 0x8dd348c; 0x8dd43ec에서 'Line2D'로> <weakref at 0x8dd348c; 0x8dd43ec에서 'Line2D'로> <weakref at 0x8dd348c; to 'Line2D'at 0x8dd43ec> 우분투 10.04에서 matplotlib 버전 0.99.1.1을 사용하고 있습니다
David Morton

1
@David Morton 방금 0.99.1로 다운 그레이드했고 이제 문제를 재현합니다. 1.0.1로만 업그레이드하는 것이 좋습니다. 0.99.x 이후 로 많은 버그 수정이있었습니다
Paul

1
여기에서 문제는 참조가 없어야 할 때 주위를 둘러싼 참조 문제 일 수 있습니다. 나는 OP가 IPython을 사용하여 물건을 테스트하고 있다고 확신합니다. 내 대답을 참조하십시오.
Vorticity 2011

66

이것은 제 동료를 위해 입력 한 매우 긴 설명입니다. 여기에서도 도움이 될 것 같습니다. 하지만 인내심을 가지십시오. 나는 당신이 마지막에 직면하고있는 진짜 문제에 도달합니다. 티저와 마찬가지로 Line2D주변에있는 개체에 대한 추가 참조가있는 문제입니다 .

경고 : 시작하기 전에 또 하나의 참고 사항이 있습니다. IPython을 사용하여 이것을 테스트하는 경우 IPython은 자체 참조를 유지하며 모든 참조가 weakref가 아닙니다. 따라서 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

이것은 또한 axes 객체의 선으로 확장됩니다.

>>> #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가리키는 객체에 대한 또 다른 참조가 있기 때문입니다 . linesID가 같지 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>

그래서 이야기의 교훈은 자신을 정리하는 것입니다. 가비지 수집을 기대하지만 그렇지 않은 경우 어딘가에 참조를 남겨 두는 것입니다.


2
정확히 내가 필요한 것. 저는 세계지도 투영 위에 산점도가있는 수천 개의지도를 플로팅하고 있습니다. 각각 3 초가 걸렸습니다! 이미 그려진지도가있는 그림을 재사용하고 결과 컬렉션을 ax.collections에서 꺼내서 1/3 초로 줄였습니다. 감사!
GaryBishop 2013 년

3
나는 이것이 현재 버전의 mpl에서 더 이상 필요하지 않다고 생각합니다. 아티스트는 remove()사물의 mpl 측면에서 그것들을 정리 하는 기능을 가지고 있으며 , 당신은 당신의 참조를 추적하기 만하면됩니다.
tacaswell

2
허,이 변경 사항이 동일한 matplotlib 버전이 무엇인지 아십니까?
Vorticity

matplotlib 애니메이션에서 여러 플롯을 사용할 때 유용하다는 것을 알았습니다. 그렇지 않으면 매우 많은 양의 메모리를 사용하게됩니다. 이제이 일을 더 빠르게합니다.
Danny Staple

14

나는 다른 포럼에서 많은 다른 답변을 시도했습니다. 나는 당신이 개발하는 기계에 달려 있다고 생각합니다. 그러나 나는 진술을 사용했다

ax.lines = []

완벽하게 작동합니다. 나는 cla()플롯에 대한 모든 정의를 삭제 하기 때문에 사용하지 않습니다.

전의.

pylab.setp(_self.ax.get_yticklabels(), fontsize=8)

하지만 여러 번 줄을 삭제하려고 시도했습니다. 또한 weakref 라이브러리를 사용하여 삭제하는 동안 해당 줄에 대한 참조를 확인했지만 아무것도 작동하지 않았습니다.

이것이 다른 사람에게 효과가 있기를 바랍니다 = D


여기에서 문제는 참조가 없어야 할 때 주위를 둘러싼 참조 문제 일 수 있습니다. 나는 OP가 IPython을 사용하여 물건을 테스트하고 있다고 확신합니다. 내 대답을 참조하십시오.
Vorticity 2011

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()

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.