Jak mogę usunąć linię (lub linie) osi matplotlib w taki sposób, aby faktycznie zbierał śmieci i zwalniał pamięć? Poniższy kod wydaje się usuwać wiersz, ale nigdy nie zwalnia pamięci (nawet w przypadku jawnych wywoł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.
Czy jest więc sposób, aby po prostu usunąć jedną linię z osi i odzyskać pamięć? To potencjalne rozwiązanie również nie działa.
python
matplotlib
plot
David Morton
źródło
źródło
To bardzo długie wyjaśnienie, które napisałem dla mojego współpracownika. Myślę, że również tutaj byłoby pomocne. Bądź jednak cierpliwy. Dochodzę do prawdziwego problemu, który masz pod koniec. Podobnie jak zwiastun, jest to kwestia dodatkowych odniesień do
Line2D
kręcących się wokół obiektów.OSTRZEŻENIE: Jeszcze jedna uwaga, zanim zaczniemy. Jeśli używasz IPythona do testowania tego, IPython zachowuje własne odwołania i nie wszystkie z nich są slabymi ref. Tak więc testowanie czyszczenia pamięci w IPythonie nie działa. Po prostu wprowadza zamieszanie.
Dobra, zaczynamy. Każdy
matplotlib
obiekt (Figure
,Axes
itp) zapewnia dostęp do swoich dziecięcych artystów poprzez różne atrybuty. Poniższy przykład robi się dość długi, ale powinien być pouczający.Zaczynamy od stworzenia
Figure
obiektu, a następnie dodajemyAxes
obiekt do tej figury. Zauważ, żeax
ifig.axes[0]
to ten sam obiekt (taki samid()
).>>> #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
Dotyczy to również linii w obiekcie osi:
>>> #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)
Gdybyś miał wywołać
plt.show()
to, co zostało zrobione powyżej, zobaczyłbyś figurę zawierającą zestaw osi i jedną linię:Teraz, chociaż widzieliśmy, że zawartość
lines
iax.lines
jest taka sama, bardzo ważne jest, aby zauważyć, że obiekt, do którego odwołuje sięlines
zmienna, nie jest tym samym, co obiekt, do którego odwołuje się obiekt, do którego odwołuje się obiekt,ax.lines
co można zobaczyć w następujący sposób:>>> id(lines), id(ax.lines) (212754584, 211335288)
W konsekwencji usunięcie elementu z
lines
nic nie robi dla bieżącego wykresu, ale usunięcie elementu zax.lines
powoduje usunięcie tej linii z bieżącego wykresu. Więc:>>> #THIS DOES NOTHING: >>> lines.pop(0) >>> #THIS REMOVES THE FIRST LINE: >>> ax.lines.pop(0)
Tak więc, jeśli miałbyś uruchomić drugą linię kodu, usunąłbyś
Line2D
obiekt zawarty wax.lines[0]
bieżącym wykresie i zniknąłby. Zauważ, że można to również zrobić poprzezax.lines.remove()
to, że możesz zapisaćLine2D
instancję w zmiennej, a następnie przekazać ją,ax.lines.remove()
aby usunąć tę linię, na przykład:>>> #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>]
Wszystko to działa
fig.axes
tak samo dobrze, jak działaax.lines
Teraz prawdziwy problem. Jeśli będziemy przechowywać odniesienie zawarte w
ax.lines[0]
doweakref.ref
obiektu, a następnie próbuje go usunąć, to zauważymy, że nie zostanie śmieci zbierane:>>> #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>
Odniesienie jest nadal aktywne! Czemu? Dzieje się tak, ponieważ istnieje jeszcze inne odniesienie do
Line2D
obiektu, na które wskazuje odniesieniewr
. Pamiętasz, jaklines
nie miał tego samego identyfikatora co,ax.lines
ale zawierał te same elementy? Cóż, w tym 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>
Więc morał tej historii jest taki, posprzątaj po sobie. Jeśli spodziewasz się, że coś zostanie zebrane jako śmieci, ale tak nie jest, prawdopodobnie zostawiasz gdzieś jakieś odniesienie.
źródło
remove()
funkcję, która oczyści je z mpl strony rzeczy, a wtedy musisz tylko śledzić swoje odniesienia.Wypróbowałem wiele różnych odpowiedzi na różnych forach. Myślę, że to zależy od maszyny, na której pracujesz. Ale użyłem tego stwierdzenia
i działa idealnie. Nie używam,
cla()
ponieważ usuwa wszystkie definicje, które wprowadziłem do wykresuDawny.
pylab.setp(_self.ax.get_yticklabels(), fontsize=8)
ale wielokrotnie próbowałem usunąć te wiersze. Używam również biblioteki slabref, aby sprawdzić odwołanie do tej linii podczas usuwania, ale nic nie działało.
Mam nadzieję, że to zadziała dla kogoś innego = D
źródło
(używając tego samego przykładu co facet powyżej)
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()
źródło
Mam nadzieję, że może to pomóc innym: powyższe przykłady używają
ax.lines
. W nowszym mpl (3.3.1) jestax.get_lines()
. To omija potrzebę dzwonieniaax.lines=[]
for line in ax.get_lines(): # ax.lines: line.remove() # ax.lines=[] # needed to complete removal when using ax.lines
źródło