Em um script no qual crio muitas figuras fix, ax = plt.subplots(...)
, recebo o aviso RuntimeWarning: Mais de 20 figuras foram abertas. As figuras criadas através da interface pyplot ( matplotlib.pyplot.figure
) são mantidas até serem fechadas explicitamente e podem consumir muita memória.
No entanto, não entendo por que recebo esse aviso, porque depois de salvar a figura com fig.savefig(...)
, a apago com fig.clear(); del fig
. Em nenhum momento do meu código, tenho mais de uma figura aberta por vez. Ainda assim, recebo o aviso sobre muitas figuras em aberto. O que isso significa / como posso evitar o aviso?
python
python-3.x
matplotlib
andreas-h
fonte
fonte
plt
completamente. Por exemplo stackoverflow.com/a/16337909/325565 (Não para ligar uma das minhas próprias respostas, mas é o que eu poderia encontrar mais rápido ...)Respostas:
Use
.clf
ou.cla
no seu objeto de figura em vez de criar uma nova figura. De @DavidZwickerSupondo que você importou
pyplot
comoplt.cla()
limpa um eixo , ou seja, o eixo atualmente ativo na figura atual. Deixa os outros eixos intocados.plt.clf()
limpa a figura atual inteira com todos os seus eixos, mas deixa a janela aberta, para que possa ser reutilizada em outras parcelas.plt.close()
fecha uma janela , que será a janela atual, se não for especificado de outra forma.plt.close('all')
fechará todas as figuras abertas.A razão que
del fig
não funciona é que apyplot
máquina de estado mantém uma referência à figura (como deve saber se é a 'figura atual'). Isso significa que, mesmo que você exclua sua referência para a figura, há pelo menos uma referência ativa e, portanto, ela nunca será coletada como lixo.Como estou pesquisando a sabedoria coletiva aqui para esta resposta, @JoeKington menciona nos comentários que
plt.close(fig)
removerão uma instância de figura específica da máquina de estado do pylab ( plt._pylab_helpers.Gcf ) e permitirão que seja coletada como lixo.fonte
clf
para afigure
classe, mas nãoclose
. Por que,del fig
na verdade, não fecha e exclui a figura?close
não funcionará no objeto figura, chame-o comoplt.close()
, em vez defig.clf()
.del fig
não funciona é que, ao fornecer um__del__
método (que seria basicamente chamadoplt.close(fig)
), acabaria causando referências circulares nesse caso específico, efig
ter um__del__
método fará com que outras coisas não sejam coletadas de lixo . (Ou essa é a minha vaga lembrança, pelo menos.) De qualquer forma, é certamente um pouco chato, mas você deve ligar emplt.close(fig)
vez dedel fig
. Em uma nota lateral, matplotlib poderia realmente usar um gerenciador de contexto para isso ...plt.close(fig)
removerá uma instância de figura específica da máquina de estado do pylab (plt._pylab_helpers.Gcf
) e permitirá que ela seja coletada no lixo.plt
é um pouco confuso e há pensamentos de como refazer um monte disso. O gerenciador de contexto é intrigante ... Consulte github.com/matplotlib/matplotlib/pull/2736 , github.com/matplotlib/matplotlib/pull/2624Aqui está um pouco mais detalhadamente para expandir a resposta de Hooked . Quando li a resposta pela primeira vez, perdi a instrução para ligar em
clf()
vez de criar uma nova figura .clf()
por si só não ajuda se você for criar outra figura.Aqui está um exemplo trivial que causa o aviso:
Para evitar o aviso, tenho que puxar a chamada para
subplots()
fora do loop. Para continuar vendo os retângulos, preciso mudarclf()
paracla()
. Isso limpa o eixo sem remover o próprio eixo.Se você estiver gerando plotagens em lotes, poderá usar ambos
cla()
eclose()
. Encontrei um problema em que um lote podia ter mais de 20 parcelas sem reclamar, mas ele reclamava após 20 lotes. Corrigi isso usandocla()
depois de cada plot eclose()
depois de cada lote.Eu medi o desempenho para ver se valia a pena reutilizar a figura dentro de um lote, e esse pequeno programa de amostra diminuiu de 41s para 49s (20% mais lento) quando acabei de ligar para
close()
cada plotagem.fonte
Se você pretende manter conscientemente muitas plotagens na memória, mas não deseja ser avisado sobre isso, pode atualizar suas opções antes de gerar números.
Isso impedirá que o aviso seja emitido sem alterar nada sobre o modo como a memória é gerenciada.
fonte
O seguinte snippet resolveu o problema para mim:
Quando
_wrapped_figure
sai do escopo, o tempo de execução chama nosso__del__()
método complt.close()
inside. Isso acontece mesmo que a exceção seja acionada após o_wrapped_figure
construtor.fonte
Isso também é útil se você deseja suprimir temporariamente o aviso:
fonte