Wie kann ich eine Zeile (oder Zeilen) einer Matplotlib-Achse so entfernen, dass tatsächlich Müll gesammelt wird und der Speicher wieder freigegeben wird? Der folgende Code scheint die Zeile zu löschen, gibt jedoch niemals den Speicher frei (selbst bei expliziten Aufrufen von 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.
Gibt es also eine Möglichkeit, nur eine Zeile aus einer Achse zu löschen und den Speicher zurückzugewinnen? Diese mögliche Lösung funktioniert auch nicht.
quelle
Dies ist eine sehr lange Erklärung, die ich für einen meiner Kollegen geschrieben habe. Ich denke, das wäre auch hier hilfreich. Sei aber geduldig. Ich komme zu dem eigentlichen Problem, das Sie gegen Ende haben. Nur als Teaser geht es darum, zusätzliche Verweise auf Ihre zu haben
Line2D
Objekte zu haben.WARNUNG: Noch ein Hinweis, bevor wir eintauchen. Wenn Sie IPython zum Testen verwenden, behält IPython eigene Referenzen bei und nicht alle sind Schwachstellen. Das Testen der Garbage Collection in IPython funktioniert also nicht. Es verwirrt nur die Dinge.
Okay, los geht's. Jedes
matplotlib
Objekt (Figure
,Axes
usw.) bietet Zugang zu seinem Kind Künstler über verschiedene Attribute. Das folgende Beispiel wird ziemlich lang, sollte aber leuchten.Wir beginnen mit der Erstellung eines
Figure
Objekts und fügenAxes
dieser Figur dann ein Objekt hinzu. Beachten Sie, dassax
undfig.axes[0]
dasselbe Objekt sind (dasselbeid()
).>>> #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
Dies erstreckt sich auch auf Linien in einem Achsenobjekt:
>>> #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)
Wenn Sie mit den oben beschriebenen Schritten anrufen
plt.show()
würden, würden Sie eine Abbildung sehen, die einen Satz Achsen und eine einzelne Zeile enthält:Obwohl wir gesehen haben, dass der Inhalt von
lines
undax.lines
derselbe ist, ist es sehr wichtig zu beachten, dass das Objekt, auf das dielines
Variable verweist, nicht dasselbe ist wie das Objekt , auf das verwiesen wirdax.lines
, wie aus dem Folgenden ersichtlich ist:>>> id(lines), id(ax.lines) (212754584, 211335288)
Infolgedessen hat das Entfernen eines Elements aus
lines
nichts mit dem aktuellen Plot zu tun, aber das Entfernen eines Elements ausax.lines
entfernt diese Linie aus dem aktuellen Plot. Damit:>>> #THIS DOES NOTHING: >>> lines.pop(0) >>> #THIS REMOVES THE FIRST LINE: >>> ax.lines.pop(0)
Wenn Sie also die zweite Codezeile ausführen würden, würden Sie das darin
Line2D
enthaltene Objektax.lines[0]
aus dem aktuellen Plot entfernen und es wäre verschwunden. Beachten Sie, dass dies auch über dieax.lines.remove()
Bedeutung erfolgen kann, dass Sie eineLine2D
Instanz in einer Variablen speichern und dann an übergeben könnenax.lines.remove()
, um diese Zeile wie folgt zu löschen:>>> #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>]
Alle oben genannten Funktionen funktionieren
fig.axes
genauso gut wie fürax.lines
Nun das eigentliche Problem hier. Wenn wir die darin enthaltene Referenz
ax.lines[0]
in einemweakref.ref
Objekt speichern und dann versuchen, sie zu löschen, werden wir feststellen, dass kein Müll gesammelt wird:>>> #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>
Die Referenz ist noch live! Warum? Dies liegt daran, dass es noch einen weiteren Verweis auf das
Line2D
Objekt gibt, auf den der Verweis inwr
verweist. Erinnern Sie sich, wie Sielines
nicht dieselbe ID hatten,ax.lines
aber dieselben Elemente enthielten? Nun, das ist das Problem.>>> #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>
Die Moral der Geschichte lautet also: Räumen Sie nach sich selbst auf. Wenn Sie erwarten, dass etwas Müll gesammelt wird, dies aber nicht ist, lassen Sie wahrscheinlich irgendwo eine Referenz hängen.
quelle
remove()
Funktion, die ihn von der MPL-Seite der Dinge befreit, und dann müssen Sie nur noch Ihre Referenzen verfolgen.Ich habe viele verschiedene Antworten in verschiedenen Foren ausprobiert. Ich denke, es hängt von der Maschine ab, die Sie entwickeln. Aber ich habe die Aussage benutzt
und funktioniert perfekt. Ich benutze nicht
cla()
weil dadurch alle Definitionen gelöscht werden, die ich für den Plot vorgenommen habeEx.
pylab.setp(_self.ax.get_yticklabels(), fontsize=8)
aber ich habe viele Male versucht, die Zeilen zu löschen. Verwenden Sie auch die schwache Referenzbibliothek, um den Verweis auf diese Zeile zu überprüfen, während ich löschte, aber nichts funktionierte für mich.
Hoffe das funktioniert für jemand anderen = D.
quelle
(am selben Beispiel wie der Typ oben)
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()
quelle
Hoffentlich kann dies anderen helfen: Die obigen Beispiele verwenden
ax.lines
. Mit neueren mpl (3.3.1) gibt esax.get_lines()
. Dadurch wird die Notwendigkeit eines Anrufs umgangenax.lines=[]
for line in ax.get_lines(): # ax.lines: line.remove() # ax.lines=[] # needed to complete removal when using ax.lines
quelle