Matplotlibプロットの線を削除する方法


85

実際にガベージコレクションを取得してメモリを解放するような方法で、matplotlib軸の1つまたは複数の行を削除するにはどうすればよいですか?以下のコードは行を削除しているように見えますが、メモリを解放することはありません(を明示的に呼び出した場合でも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.

では、軸から1行を削除して、メモリを元に戻す方法はありますか? この潜在的な解決策も機能しません。

回答:


71

私はそれの組み合わせを示しています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」へ> <0x8dd348cのweakref; 0x8dd43ecの「Line2D」へ> <0x8dd348cのweakref; 0x8dd43ecの「Line2D」へ> ubuntu 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を使用してテストを行っていたに違いありません。私の答えを見てください。
渦度2012年

67

これは私が私の同僚のためにタイプした非常に長い説明です。ここでも参考になると思います。ただし、しばらくお待ちください。私はあなたが終わりに向かって抱えている本当の問題に到達します。ティーザーと同じように、Line2Dオブジェクトへの追加の参照がぶら下がっているという問題です。

警告:詳しく説明する前にもう1つ注意してください。IPythonを使用してこれをテストしている場合、IPythonは独自の参照を保持し、すべてがweakrefであるとは限りません。そのため、IPythonでガベージコレクションをテストすることはできません。それは問題を混乱させるだけです。

さて、ここに行きます。各matplotlibオブジェクト(FigureAxesなど)は、さまざまな属性を介してその子アーティストへのアクセスを提供します。次の例はかなり長くなっていますが、明るくなるはずです。

まず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()上記で行ったことを使用して呼び出すと、軸のセットと1本の線を含む図が表示されます。

軸のセットと1本の線を含む図

今、私たちは内容を見ましたながらlinesax.lines同じで、参照するオブジェクトことに注意することが非常に重要であるlines変数がでreverencedオブジェクトと同じではありません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)

したがって、コードの2行目を実行すると、現在のプロットに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>]

軸のセットと2本の線を含む図

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

軸のセットと2行目のみを含む図

上記のすべては、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同じ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>

だから、話の教訓は、自分の後で片付けることです。何かがガベージコレクションされることを期待しているのにそうではない場合は、参照がどこかにぶら下がっている可能性があります。


2
まさに私が必要としていたもの。私は何千もの地図をプロットしており、それぞれが世界地図投影法の上に散布図を持っています。彼らはそれぞれ3秒かかっていました!マップが既に描画されている図を再利用し、結果のコレクションをax.collectionsからポップすることで、1/3秒に短縮しました。ありがとう!
GaryBishop 2013年

4
これは、現在のバージョンのmplでは不要になったと思います。アーティストには、remove()物事のmpl側からそれらを一掃する機能があり、参照を追跡するだけで済みます。
tacaswell 2013年

2
ええと、これがどのバージョンのmatplotlibで同じように変更されるのか、何か考えはありますか?
渦度

これは、matplotlibアニメーションで一連のプロットを使用するときに役立つことがわかりました。そうしないと、非常に大量のメモリが使用されることになります。今、これをより速くするために。
ダニーステープル

14

私はさまざまなフォーラムでさまざまな答えを試しました。開発中のマシンにもよると思います。しかし、私はステートメントを使用しました

ax.lines = []

完璧に動作します。cla()プロットに対して行ったすべての定義が削除されるため、使用しません

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

しかし、私は何度も行を削除しようとしました。また、weakrefライブラリを使用して、削除中にその行への参照を確認しましたが、何も機能しませんでした。

これが他の誰かのために働くことを願っています= D


ここでの問題は、参照が本来あるべきではないときにぶら下がっている問題である可能性があります。OPがIPythonを使用してテストを行っていたに違いありません。私の答えを見てください。
渦度2012年

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.