[python] 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.

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



답변

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


답변

이것은 제 동료를 위해 입력 한 매우 긴 설명입니다. 여기에서도 도움이 될 것 같습니다. 하지만 인내심을 가지십시오. 나는 당신이 마지막에 직면하고있는 진짜 문제에 도달합니다. 티저와 마찬가지로 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>

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


답변

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

ax.lines = []

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

전의.

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

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

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


답변

(위의 사람과 같은 예를 사용하여)

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


답변

다른 사람들에게 도움이되기를 바랍니다 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


답변