web-dev-qa-db-fra.com

Comment supprimer des lignes dans un tracé Matplotlib

Comment puis-je supprimer une ligne (ou des lignes) d'un axe matplotlib de manière à ce que les ordures soient réellement collectées et que la mémoire soit restituée? Le code ci-dessous semble supprimer la ligne mais ne libère jamais la mémoire (même avec des appels explicites à 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.

Y a-t-il moyen de supprimer une ligne d'un axe et de récupérer la mémoire? Cette solution potentielle ne fonctionne pas non plus.

66
David Morton

Je montre qu’une combinaison de lines.pop(0)l.remove() et del l fait le tour.

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)

J'ai vérifié votre grand ensemble de données et la libération de la mémoire est également confirmée sur le moniteur du système.

Bien sûr, la méthode la plus simple (sans problème) serait de la supprimer de la liste et d’appeler remove sur l’objet de ligne sans créer de référence précise:

lines.pop(0).remove()
52
Paul

C'est une très longue explication que j'ai dactylographiée pour un de mes collègues. Je pense que ce serait utile ici aussi. Soyez patient, cependant. J'arrive au vrai problème que vous rencontrez vers la fin. À titre d’affichage, il est difficile d’avoir des références supplémentaires à vos objets Line2D.

ATTENTION: Une autre remarque avant de plonger. Si vous utilisez IPython pour tester cela, IPython conserve ses propres références et toutes ne sont pas des références faibles. Donc, tester la récupération de place dans IPython ne fonctionne pas. Cela déroute les choses.

Ok, on y va. Chaque objet matplotlib (Figure, Axes, etc.) donne accès à ses artistes enfants via divers attributs. L'exemple suivant devient assez long, mais devrait être éclairant.

Nous commençons par créer un objet Figure, puis nous ajoutons un objet Axes à cette figure. Notez que ax et fig.axes[0] Sont le même objet (même 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

Cela s'étend également aux lignes d'un objet d'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)

Si vous appeliez plt.show() en utilisant ce qui a été fait ci-dessus, vous verriez une figure contenant un ensemble d'axes et une seule ligne:

A figure containing a set of axes and a single line

Maintenant, alors que nous avons vu que le contenu de lines et de ax.lines Est le même, il est très important de noter que l'objet référencé par la variable lines n'est pas le même comme l'objet respecté par ax.lines comme on peut le voir par ce qui suit:

>>> id(lines), id(ax.lines)
(212754584, 211335288)

En conséquence, la suppression d'un élément de lines ne fait rien pour le tracé actuel, mais la suppression d'un élément de ax.lines Supprime cette ligne du tracé actuel. Alors:

>>> #THIS DOES NOTHING:
>>> lines.pop(0)

>>> #THIS REMOVES THE FIRST LINE:
>>> ax.lines.pop(0)

Donc, si vous deviez exécuter la deuxième ligne de code, vous supprimeriez l'objet Line2D Contenu dans ax.lines[0] Du graphique actuel et il disparaîtrait. Notez que cela peut également être fait via ax.lines.remove(), ce qui signifie que vous pouvez enregistrer une instance Line2D Dans une variable, puis le transmettre à ax.lines.remove() pour supprimer cette ligne, comme ceci :

>>> #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>]

A figure containing a set of axes and two lines

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

A figure containing a set of axes and only the second line

Tout ce qui précède fonctionne pour fig.axes Aussi bien que pour ax.lines

Maintenant, le vrai problème ici. Si nous stockons la référence contenue dans ax.lines[0] Dans un objet weakref.ref, Puis tentons de la supprimer, nous remarquerons qu'elle n'est pas récupérée:

>>> #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 référence est toujours en direct! Pourquoi? En effet, il existe encore une autre référence à l'objet Line2D Sur laquelle pointe la référence dans wr. Rappelez-vous comment lines n'avait pas le même identifiant que ax.lines Mais contenait les mêmes éléments? Eh bien, c'est le problème.

>>> #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>

Ainsi, la morale de l'histoire est, nettoyez-vous après vous-même. Si vous vous attendez à ce que quelque chose soit ramassé mais que ce ne soit pas le cas, vous laisserez probablement une référence traîner quelque part.

55
Vorticity

J'ai essayé beaucoup de réponses différentes dans différents forums. Je suppose que cela dépend de la machine que vous développez. Mais j'ai utilisé la déclaration

ax.lines = []

et fonctionne parfaitement. Je n'utilise pas cla () car il supprime toutes les définitions que j'ai apportées à l'intrigue

Ex.

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

mais j'ai essayé de supprimer les lignes plusieurs fois. Également utiliser la bibliothèque faible référence pour vérifier la référence à cette ligne pendant la suppression, mais rien ne fonctionnait pour moi.

J'espère que ça marche pour quelqu'un d'autre = D

11
Jeronimo Schreyer

(en utilisant le même exemple que le gars ci-dessus)

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