web-dev-qa-db-fra.com

avertissement sur trop de figures ouvertes

Dans un script où je crée plusieurs figures avec fix, ax = plt.subplots(...), je reçois l'avertissement RuntimeWarning: plus de 20 figures ont été ouvertes. Les figures créées via l'interface pyplot (matplotlib.pyplot.figure) Sont conservés jusqu'à ce qu'ils soient explicitement fermés et peuvent consommer trop de mémoire.

Cependant, je ne comprends pas pourquoi Je reçois cet avertissement, car après avoir enregistré le chiffre avec fig.savefig(...), je le supprime avec fig.clear(); del fig. À aucun moment dans mon code, j'ai plus d'une figure ouverte à la fois. Malgré tout, je reçois un avertissement concernant trop de chiffres ouverts. Qu'est-ce que cela signifie/comment puis-je éviter de recevoir l'avertissement?

114
andreas-h

Utilisez .clf Ou .cla Sur votre objet figure au lieu de créer un nouveau chiffre . De @ DavidZwicker

En supposant que vous ayez importé pyplot comme

import matplotlib.pyplot as plt

plt.cla() efface un axe , c’est-à-dire l’axe actuellement actif dans la figure actuelle. Cela laisse les autres axes intacts.

plt.clf() efface la totalité de la figure courante avec tous ses axes, mais laisse la fenêtre ouverte, de sorte qu'elle puisse être réutilisée pour d'autres tracés.

plt.close() ferme une fenêtre , qui sera la fenêtre actuelle, sauf indication contraire. plt.close('all') ferme toutes les figures ouvertes.

La raison pour laquelle del fig Ne fonctionne pas, c'est que la machine à états pyplot conserve une référence à la figure autour (comme il le doit si elle va savoir quelle est la "figure actuelle"). Cela signifie que même si vous supprimez votre référence à la figure, il y a au moins une référence active, elle ne sera donc jamais récupérée.

Comme je m'interroge sur la sagesse collective de cette réponse, @JoeKington mentionne dans les commentaires que plt.close(fig) supprimera une instance de figure spécifique de la machine à états de pylab ( plt._pylab_helpers.Gcf ) et autorisez-le à être nettoyé.

148
Hooked

Voici un peu plus de détails pour développer réponse de Hooked . Lorsque j'ai lu cette réponse pour la première fois, j'ai raté l'instruction d'appeler clf() au lieu de créer un nouveau chiffre . clf() à lui seul n'aide pas si vous créez ensuite une autre figure.

Voici un exemple trivial qui provoque l'avertissement:

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

Pour éviter cet avertissement, je dois extraire l'appel de subplots() à l'extérieur de la boucle. Afin de continuer à voir les rectangles, je dois passer clf() à cla(). Cela efface l'axe sans supprimer l'axe lui-même.

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

Si vous générez des parcelles par lots, vous devrez peut-être utiliser à la fois cla() et close(). J'ai rencontré un problème dans lequel un lot pouvait comporter plus de 20 parcelles sans se plaindre, mais se plaindrait au bout de 20 lots. J'ai corrigé cela en utilisant cla() après chaque tracé, et close() après chaque lot.

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

J'ai mesuré la performance pour voir s'il valait la peine de réutiliser le chiffre dans un lot et ce petit exemple de programme a ralenti de 41 à 49 s (20% de moins) lorsque je viens d'appeler close() après chaque tracé.

20
Don Kirkby

Si vous avez l'intention de garder sciemment de nombreuses parcelles en mémoire, mais que vous ne souhaitez pas en être averti, vous pouvez mettre à jour vos options avant de générer des figures.

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

Cela empêchera l'émission de l'avertissement sans rien changer à la façon dont la mémoire est gérée.

10
mightypile