Как я могу удалить строку (или строки) осей matplotlib таким образом, чтобы она фактически собирала мусор и освобождала память обратно? Приведенный ниже код, кажется, удаляет строку, но никогда не освобождает память (даже при явных вызовах 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.
Так есть ли способ просто удалить одну строку из осей и вернуть память? Это потенциальное решение тоже не работает.
python
matplotlib
plot
Дэвид Мортон
источник
источник
Это очень длинное объяснение, которое я напечатал для своего коллеги. Думаю, здесь тоже было бы полезно. Но будьте терпеливы. Я подхожу к реальной проблеме, с которой вы столкнулись ближе к концу. Как тизер, это проблема наличия дополнительных ссылок на ваши
Line2D
объекты.ПРЕДУПРЕЖДЕНИЕ. Еще одно замечание, прежде чем мы углубимся. Если вы используете IPython для проверки этого, IPython сохраняет собственные ссылки, и не все из них являются слабыми ссылками. Итак, тестирование сборки мусора в IPython не работает. Это просто сбивает с толку.
Хорошо, поехали. Каждый
matplotlib
объект (Figure
,Axes
и т. Д.) Предоставляет доступ к своим дочерним художникам через различные атрибуты. Следующий пример становится довольно длинным, но должен проливать свет.Мы начинаем с создания
Figure
объекта, а затем добавляемAxes
объект к этой фигуре. Обратите внимание, чтоax
иfig.axes[0]
являются одним и тем же объектом (одинаковым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
Это также распространяется на строки в объекте оси:
>>> #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)
Если бы вы вызывали,
plt.show()
используя то, что было сделано выше, вы бы увидели фигуру, содержащую набор осей и одну строку:Теперь, хотя мы увидели, что содержимое
lines
иax.lines
является одинаковым, очень важно отметить, что объект, на который ссылаетсяlines
переменная, не совпадает с объектом, на который обращается внимание,ax.lines
что можно увидеть из следующего:>>> id(lines), id(ax.lines) (212754584, 211335288)
Как следствие, удаление элемента из
lines
текущего графика ничего не делает, но удаление элемента изax.lines
удаляет эту строку из текущего графика. Так:>>> #THIS DOES NOTHING: >>> lines.pop(0) >>> #THIS REMOVES THE FIRST LINE: >>> ax.lines.pop(0)
Итак, если вы запустите вторую строку кода, вы удалите
Line2D
объект, содержащийся вax.lines[0]
текущем графике, и он исчезнет. Обратите внимание, что это также можно сделать,ax.lines.remove()
имея в виду, что вы можете сохранитьLine2D
экземпляр в переменной, а затем передать его,ax.lines.remove()
чтобы удалить эту строку, например:>>> #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>]
Все вышеперечисленное работает
fig.axes
так же хорошо, как и дляax.lines
Итак, настоящая проблема здесь. Если мы сохраняем ссылку , содержащуюся в
ax.lines[0]
вweakref.ref
объект, а затем попытаться удалить его, мы заметим , что он не получает мусора:>>> #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>
Ссылка все еще жива! Почему? Это потому, что есть еще одна ссылка на
Line2D
объект, на которыйwr
указывает ссылка . Помните, какlines
не было такого же идентификатора, как,ax.lines
но содержали те же элементы? Ну вот и проблема.>>> #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>
Итак, мораль этой истории такова: убирайте за собой. Если вы ожидаете, что что-то будет собрано сборщиком мусора, но это не так, вы, скорее всего, оставите где-то висеть ссылку.
источник
remove()
функция, которая очистит их от mpl-стороны вещей, и тогда вам нужно будет только отслеживать ваши ссылки.Я перепробовал много разных ответов на разных форумах. Я думаю, это зависит от машины, которую вы разрабатываете. Но я использовал заявление
и работает отлично. Я не использую,
cla()
потому что он удаляет все определения, которые я сделал для сюжетаEx.
pylab.setp(_self.ax.get_yticklabels(), fontsize=8)
но я много раз пытался удалить строки. Также с помощью библиотеки weakref проверять ссылку на эту строку, пока я удалял, но у меня ничего не работало.
Надеюсь, это сработает для кого-то другого = D
источник
(используя тот же пример, что и парень выше)
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()
источник
Надеюсь, это поможет другим: в приведенных выше примерах используется
ax.lines
. В более поздних версиях mpl (3.3.1) есть файлыax.get_lines()
. Это избавляет от необходимости звонитьax.lines=[]
for line in ax.get_lines(): # ax.lines: line.remove() # ax.lines=[] # needed to complete removal when using ax.lines
источник