Как удалить строки на графике Matplotlib

Как я могу удалить линию (или строки) осей 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.

Итак, есть способ просто удалить одну строку из осей и вернуть память? Это потенциальное решение также не работает.

Ответ 1

Я показываю, что комбинация lines.pop(0) l.remove() и del l делает трюк.

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)

Я проверил ваш большой набор данных, и релиз памяти также подтвержден на системном мониторе.

Конечно, более простой способ (если не проблема) - вытащить его из списка и вызвать remove в объекте линии без создания жесткой ссылки на него:

lines.pop(0).remove()

Ответ 2

Это очень длинное объяснение, которое я набрал для моего коллеги. Я думаю, что здесь было бы полезно. Будьте терпеливы. Я добираюсь до настоящего вопроса, который у вас есть к концу. Подобно тизеру, это вопрос о дополнительных ссылках на ваши объекты 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(), используя то, что было сделано выше, вы увидите фигуру, содержащую набор осей и одну строку:

A figure containing a set of axes and a single line

Теперь, когда мы видели, что содержимое 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>]

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

Все вышеприведенные работы для 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>

Итак, мораль этой истории, очистите себя. Если вы ожидаете, что что-то будет собирать мусор, но это не так, вы, вероятно, оставите где-нибудь ссылку.

Ответ 3

Я пробовал много разных ответов на разных форумах. Я думаю, это зависит от машины, которую вы развиваете. Но я использовал выражение

ax.lines = []

и работает отлично. Я не использую cla(), потому что он удаляет все определения, которые я сделал для графика

Исх.

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

но я много раз пытался удалять строки. Также, используя библиотеку weakref, чтобы проверить ссылку на эту строку во время удаления, но ничего не работало для меня.

Надеюсь, что это работает для кого-то другого = D

Ответ 4

(используя тот же пример, что и выше)

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