¿Cómo puedo eliminar una línea (o líneas) de los ejes de un matplotlib de tal manera que realmente se recolecta la basura y se libera la memoria? El siguiente código parece eliminar la línea, pero nunca libera la memoria (incluso con llamadas explícitas a 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.
Entonces, ¿hay alguna manera de eliminar una línea de un eje y recuperar la memoria? Esta posible solución tampoco funciona.
fuente
Esta es una explicación muy larga que escribí para un compañero de trabajo. Creo que aquí también sería útil. Pero tenga paciencia. Llego al problema real que está teniendo hacia el final. Solo como un adelanto, se trata de tener referencias adicionales a sus
Line2D
objetos merodeando.ADVERTENCIA: Una nota más antes de sumergirnos. Si está utilizando IPython para probar esto, IPython mantiene sus propias referencias y no todas son referencias débiles. Entonces, probar la recolección de basura en IPython no funciona. Simplemente confunde las cosas.
Bien, aquí vamos. Cada
matplotlib
objeto (Figure
,Axes
, etc.) proporciona acceso a sus niños artistas a través de varios atributos. El siguiente ejemplo se está volviendo bastante largo, pero debería ser esclarecedor.Comenzamos creando un
Figure
objeto, luego agregamos unAxes
objeto a esa figura. Tenga en cuenta queax
yfig.axes[0]
son el mismo objeto (mismoid()
).>>> #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
Esto también se extiende a las líneas en un objeto de ejes:
>>> #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)
Si llamara
plt.show()
usando lo que se hizo anteriormente, verá una figura que contiene un conjunto de ejes y una sola línea:Ahora, si bien hemos visto que el contenido de
lines
yax.lines
es el mismo, es muy importante notar que el objeto al que hace referencia lalines
variable no es el mismo que el objeto reverenciado porax.lines
como se puede ver en lo siguiente:>>> id(lines), id(ax.lines) (212754584, 211335288)
Como consecuencia, eliminar un elemento de
lines
no afecta al gráfico actual, pero eliminar un elemento deax.lines
elimina esa línea del gráfico actual. Entonces:>>> #THIS DOES NOTHING: >>> lines.pop(0) >>> #THIS REMOVES THE FIRST LINE: >>> ax.lines.pop(0)
Entonces, si tuviera que ejecutar la segunda línea de código, eliminaría el
Line2D
objeto contenido enax.lines[0]
el gráfico actual y desaparecería. Tenga en cuenta que esto también se puede hacer,ax.lines.remove()
lo que significa que puede guardar unaLine2D
instancia en una variable, luego pasarla aax.lines.remove()
para eliminar esa línea, así:>>> #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>]
Todo lo anterior funciona
fig.axes
tan bien como funciona paraax.lines
Ahora, el verdadero problema aquí. Si almacenamos la referencia contenida en
ax.lines[0]
unweakref.ref
objeto, luego intentamos eliminarlo, notaremos que no se recolecta la basura:>>> #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>
¡La referencia aún está viva! ¿Por qué? Esto se debe a que todavía hay otra referencia al
Line2D
objeto al quewr
apunta la referencia . ¿Recuerdalines
que no tenía el mismo IDax.lines
pero contenía los mismos elementos? Bueno, ese es el problema.>>> #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>
Entonces, la moraleja de la historia es, limpia después de ti mismo. Si espera que algo sea recolectado como basura pero no lo es, es probable que esté dejando una referencia en algún lugar.
fuente
remove()
función que los limpiará del lado mpl de las cosas, y luego solo necesita realizar un seguimiento de sus referencias.Probé muchas respuestas diferentes en diferentes foros. Supongo que depende de la máquina que desarrolles. Pero he usado la declaración
y funciona perfectamente. No lo uso
cla()
porque elimina todas las definiciones que hice en la tramaEx.
pylab.setp(_self.ax.get_yticklabels(), fontsize=8)
pero he intentado borrar las líneas muchas veces. También usé la biblioteca débilref para verificar la referencia a esa línea mientras estaba eliminando, pero nada funcionó para mí.
Espero que esto funcione para alguien más = D
fuente
(usando el mismo ejemplo que el chico de arriba)
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()
fuente
Con suerte, esto puede ayudar a otros: Los ejemplos anteriores usan
ax.lines
. Con mpl más reciente (3.3.1), hayax.get_lines()
. Esto evita la necesidad de llamarax.lines=[]
for line in ax.get_lines(): # ax.lines: line.remove() # ax.lines=[] # needed to complete removal when using ax.lines
fuente