Подтвердить что ты не робот

Предупреждение о слишком большом количестве открытых фигур

В script, где я создаю много цифр с fix, ax = plt.subplots(...), я получаю предупреждение RuntimeWarning: открыто более 20 цифр. Цифры, созданные через интерфейс pyplot (matplotlib.pyplot.figure), сохраняются до явного закрытия и могут потреблять слишком много памяти.

Однако я не понимаю, почему я получил это предупреждение, потому что после сохранения цифры с fig.savefig(...) я удаляю ее с помощью fig.clear(); del fig. Ни в коем случае в моем коде не было ни одной фигуры, открытой одновременно. Тем не менее, я получаю предупреждение о слишком многих открытых фигурах. Что это значит/как я могу избежать предупреждения?

4b9b3361

Ответ 1

Используйте .clf или .cla на фигурном объекте вместо создания нового рисунка. Из @DavidZwicker

Предполагая, что вы импортировали pyplot в качестве

import matplotlib.pyplot as plt

plt.cla() очищает ось, то есть текущую активную ось на текущем рисунке. Это оставляет другие оси нетронутыми.

plt.clf() очищает всю текущую цифру всеми ее осями, но оставляет открытое окно таким, что оно может быть повторно использовано для других графиков.

plt.close() закрывает окно, которое будет текущим окном, если не указано иначе. plt.close('all') закроет все открытые цифры.

Причина, по которой del fig не работает, заключается в том, что состояние-машина pyplot хранит ссылку на фигуру вокруг (как и должно быть, если она будет знать, что такое "текущая цифра" ). Это означает, что даже если вы удаляете свой рефлектор на фигуре, есть хотя бы один live ref, поэтому он никогда не будет собираться с мусором.

Поскольку я отвечаю на коллективную мудрость здесь для этого ответа, @JoeKington упоминает в комментариях, что plt.close(fig) удалит конкретную (plt._pylab_helpers.Gcf) и разрешить ему собирать мусор.

Ответ 2

Здесь немного более подробно, чтобы перейти на "Захваченный ответ" . Когда я впервые прочитал этот ответ, я пропустил инструкцию называть clf() вместо создания новой фигуры. clf() сам по себе не помогает, если вы затем идете и создаете другую фигуру.

Вот тривиальный пример, который вызывает предупреждение:

from matplotlib import pyplot as plt, patches
import os


def main():
    path = 'figures'
    for i in range(21):
        _fig, ax = plt.subplots()
        x = range(3*i)
        y = [n*n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.clf()
    print('Done.')

main()

Чтобы избежать предупреждения, я должен вывести вызов subplots() за пределы цикла. Чтобы видеть прямоугольники, мне нужно переключить clf() на cla(). Это очищает ось без удаления самой оси.

from matplotlib import pyplot as plt, patches
import os


def main():
    path = 'figures'
    _fig, ax = plt.subplots()
    for i in range(21):
        x = range(3*i)
        y = [n*n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.cla()
    print('Done.')

main()

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

from matplotlib import pyplot as plt, patches
import os


def main():
    for i in range(21):
        print('Batch {}'.format(i))
        make_plots('figures')
    print('Done.')


def make_plots(path):
    fig, ax = plt.subplots()
    for i in range(21):
        x = range(3 * i)
        y = [n * n for n in x]
        ax.add_patch(patches.Rectangle(xy=(i, 1), width=i, height=10))
        plt.step(x, y, linewidth=2, where='mid')
        figname = 'fig_{}.png'.format(i)
        dest = os.path.join(path, figname)
        plt.savefig(dest)  # write image to file
        plt.cla()
    plt.close(fig)


main()

Я измерил производительность, чтобы увидеть, стоит ли повторно использовать фигуру в пакете, и эта небольшая программа-образец замедлилась с 41 до 49 секунд (на 20% медленнее), когда я только что назвал close() после каждого графика.

Ответ 3

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

import matplotlib.pyplot as plt
plt.rcParams.update({'figure.max_open_warning': 0})

Это предотвратит выдачу предупреждения без каких-либо изменений в способе управления памятью.