Comment supprimer des lignes dans un graphe Matplotlib

Comment puis-je enlever une (ou des) Ligne (s) d'un axe matplotlib de manière à ce que les ordures soient ramassées et que la mémoire revienne? Le code ci-dessous semble supprimer la ligne, mais ne libère jamais la mémoire (même avec des appels explicites au 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.

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

60
demandé sur Community 2011-02-13 04:19:11

4 réponses

je montre qu'une combinaison de lines.pop(0) l.remove() et del l fait l'affaire.

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 sortie de la mémoire est également confirmée sur le moniteur système.

bien sûr, la façon la plus simple (quand il n'y a pas de dépannage) serait de le sortir de la liste et d'appeler remove sur l'objet line sans créer une référence dure à lui:

lines.pop(0).remove()
45
répondu Paul 2011-02-13 02:31:54

c'est une très longue explication que j'ai tapée pour un de mes collègues. Je pense qu'il serait utile ici aussi. Être patient, si. J'en arrive au vrai problème que vous avez vers la fin. Tout comme un teaser, c'est une question d'avoir des références supplémentaires à vos Line2D objets autour.

avertissement: une autre note avant de plonger. Si vous utilisez IPython pour tester ceci, IPython garde ses propres références et tous ne sont pas weakrefs. Donc, tester la collecte des ordures à IPython ne fonctionne pas. Ça confond juste les choses.

OK, c'est parti. Chaque objet matplotlib ( Figure , Axes , etc.) donne accès à ses enfants artistes par le biais de divers attributs. L'exemple suivant est très long, mais devrait être éclairante.

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

ceci s'étend également aux lignes dans un objet 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 deviez appeler 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, tandis que nous avons vu que le contenu de lines et 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 que l'objet révéré par ax.lines comme peut être vu par ce qui suit:

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

par conséquent, supprimer un élément de lines ne fait rien au tracé actuel, mais supprimer un élément de ax.lines supprime cette ligne du tracé actuel. So:

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

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

donc, si vous étiez pour exécuter la deuxième ligne de code, vous retireriez l'objet Line2D contenu dans ax.lines[0] de la parcelle actuelle et il serait parti. Notez que cela peut aussi être fait via ax.lines.remove() ce qui signifie que vous pouvez enregistrer une instance Line2D dans une variable, puis la passer à ax.lines.remove() pour supprimer cette ligne, comme suit:

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

tous les travaux ci-dessus 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'il ne reçoit pas les ordures collectées:

>>> #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? C'est parce qu'il y a encore une autre référence à la Line2D objet de référence dans wr points à. Rappelez-vous comment lines n'avait pas le même ID que ax.lines mais contenait les mêmes éléments? Eh bien, c'est ça 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>

Donc, la morale de l'histoire est, nettoyer après vous. Si vous vous attendez à ce que quelque chose soit ramassée, mais ce n'est pas le cas, vous êtes susceptible de laisser une référence traîner quelque part.

49
répondu Vorticity 2012-11-27 21:50:41

j'ai essayé beaucoup de réponses différentes dans différents forums. Je suppose que ça 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 faites à l'intrigue

ex.

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

mais j'ai essayé de supprimer les lignes plusieurs fois. J'utilise aussi la bibliothèque weakref pour vérifier la référence à cette ligne pendant que j'étais supprimer mais rien n'a fonctionné pour moi.

Espérons que cela fonctionne pour quelqu'un d'autre =D

9
répondu Jeronimo Schreyer 2012-03-27 19:29:37

(en utilisant le même exemple que le type 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()
2
répondu Jeronimo 2011-11-04 14:05:17