çok fazla açık figür hakkında uyarı


166

Birçok şekil oluşturduğum bir komut dosyasında fix, ax = plt.subplots(...), RuntimeWarning: 20'den fazla şekil açıldı uyarısını alıyorum. Pyplot arabirimi ( matplotlib.pyplot.figure) ile oluşturulan şekiller açıkça kapatılana kadar saklanır ve çok fazla bellek tüketebilir.

Ancak, bu uyarıyı neden aldığımı anlamıyorum , çünkü figürü kaydettikten sonra ile fig.savefig(...)silerim fig.clear(); del fig. Kodumun hiçbir noktasında, aynı anda birden fazla rakam var. Yine de, çok fazla açık figür hakkında uyarı alıyorum. Bu ne anlama geliyor / uyarıyı almaktan nasıl kaçınabilirim?


9
Bunların çoğunu yapıyorsanız ve etkileşimli bir şey görüntülemiyorsanız, plttamamen atlamanız daha iyi olabilir . Örneğin stackoverflow.com/a/16337909/325565 (Kendi cevaplarımdan birini takmamak için, ama en hızlı bulabildiğim ...)
Joe Kington

1
@JoeKington teşekkür ederim bu daha iyi bir çözüm
hihell

Joe Kington'un cevabı ana cevap listesinde olmalıdır. Don Kirby'nin bahsettiği programı yavaşlatan plt.close () ile sorunu çözer ve çözer.
NatalieL

Yanıtlar:


199

Yeni bir şekil oluşturmak yerine şekil nesnenizde .clfveya kullanın . Gönderen @DavidZwicker.cla

pyplotOlarak içe aktardığınız varsayılarak

import matplotlib.pyplot as plt

plt.cla()bir ekseni , yani geçerli şekilde aktif olan ekseni temizler . Diğer eksenlere dokunulmaz.

plt.clf()tüm mevcut şekli tüm eksenleri ile temizler , ancak pencereyi açık bırakır, böylece diğer araziler için tekrar kullanılabilir.

plt.close()aksi belirtilmedikçe geçerli pencere olacak bir pencereyi kapatır . plt.close('all')tüm açık rakamları kapatacak.

del figİşe yaramamasının nedeni , pyplotdevlet makinesinin etrafındaki şekle bir referans tutmasıdır ('mevcut rakamın ne olduğunu bilecekse olması gerektiği gibi). Silmek bile bu araçlar sizin rakama ref, dolayısıyla çöp toplanan asla, en az bir canlı ref yoktur.

Bu cevap için burada kolektif bilgeliği yokladığımdan, @JoeKington, yorumlarda plt.close(fig), pylab durum makinesinden ( plt._pylab_helpers.Gcf ) belirli bir şekil örneğini kaldıracak ve çöp toplanmasına izin verecek yorumlardan bahsediyor .


1
Mhh. Sınıf clfiçin var figureama değil close. del figŞekli neden kapatıp silmiyor?
andreas-h

2
@ andreas-h Tahminim: kendi işleyicileri olan bir pencere yöneticisi gibi karmaşık bir şey için, kapsam dışında bir şey koymaktan daha fazla temizlik gerekebilir. closeŞekil nesnesi üzerinde çalışmayan hakkınız plt.close()yerine bunun yerine şöyle deyin fig.clf().
Kancalı

5
@ andreas-h - Temel olarak, del figbunun işe yaramamasının nedeni, ona bir __del__yöntem vermenin (temel olarak çağıracak plt.close(fig)) bu özel durumda dairesel referanslara neden olacak ve figbir __del__yönteme sahip olmanın başka şeylerin çöp toplanmamasına neden olacağıdır. . (Ya da bu zaten benim belirsiz anımsamam.) Her neyse, kesinlikle biraz sinir bozucu, ama plt.close(fig)bunun yerine aramalısın del fig. Yan notta, matplotlib bunun için gerçekten bir içerik yöneticisi kullanabilir ...
Joe Kington

6
@Hooked - Biraz daha açık hale getirmek için, sorunuzu plt.close(fig), pylab durum makinesinden ( plt._pylab_helpers.Gcf) belirli bir şekil örneğini kaldıracak ve çöp toplanmasına izin vereceğini belirtmek için düzenleyebilirsiniz.
Joe Kington

2
@JoeKington pltbiraz karışıklık ve bir demet nasıl yeniden yapmak için düşünceler var. İçerik yöneticisi ilgi çekici .... Bkz. Github.com/matplotlib/matplotlib/pull/2736 , github.com/matplotlib/matplotlib/pull/2624
tacaswell

33

İşte Hooked'in cevabını genişletmek için biraz daha detay . Bu cevabı ilk okuduğumda, clf() yeni bir figür oluşturmak yerine arama talimatını kaçırdım . clf()tek başına gidip başka bir figür yaratırsanız yardımcı olmaz.

Uyarıya neden olan önemsiz bir örnek:

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

Uyarıyı önlemek için, çağrıyı subplots()döngünün dışına çekmem gerekiyor. Dikdörtgenler görmeye devam etmek için, ben anahtara ihtiyaç clf()için cla(). Bu, ekseni kendisi çıkarmadan ekseni temizler.

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

Eğer gruplar halinde araziler elde edip, her iki kullanmanız gerekebilir cla()ve close(). Bir partinin şikayet etmeden 20'den fazla arsaya sahip olabileceği bir sorunla karşılaştım, ancak 20 partiden sonra şikayet ediyorum. Bunu cla()her arsadan close()sonra ve her partiden sonra kullanarak düzelttim .

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

Performansı, bir parti içindeki figürü tekrar kullanmaya değip değmeyeceğini görmek için ölçtüm ve bu küçük örnek program close()her arsadan sonra aradığımda 41'den 49'a (% 20 daha yavaş) yavaşladı .


Bu harika bir cevap. Kabul edilen cevap eldeki gerçek sorunu, yani bellek tüketimini ele almıyor.
Kyle

24

Birçok grafiği bilerek bellekte tutmak istiyorsanız, ancak bunun hakkında uyarılmak istemiyorsanız, rakam oluşturmadan önce seçeneklerinizi güncelleyebilirsiniz.

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

Bu, belleğin yönetilme şekliyle ilgili hiçbir şeyi değiştirmeden uyarının gönderilmesini önleyecektir.


Jupyter ortamında, grafiği gösteren hücre var olduğu sürece bellek ayırma devam eder mi?
matanster

2
@matanster, bunu kendi sorusu olarak gönderirdim. Yanıtlamaya başladım, sonra jupyter'ın dürüstçe cevap verecek çekirdek yönetimi hakkında gerçekten bilmediğimi fark ettim.
mightypile

@matanster Çekirdek kullanıcı tarafından açıkça kapatılana kadar tüm değişkenler ve bunlar için ayrılan bellek var olur. Hücrelere bağlı değildir. Daha yeni Jupyter Hub'da, sistem çekirdekleri kapatabilir (yapılandırılabilir).
greatvovan

0

Aşağıdaki kod parçası sorunu benim için çözdü:


class FigureWrapper(object):
    '''Frees underlying figure when it goes out of scope. 
    '''

    def __init__(self, figure):
        self._figure = figure

    def __del__(self):
        plt.close(self._figure)
        print("Figure removed")


# .....
    f, ax = plt.subplots(1, figsize=(20, 20))
    _wrapped_figure = FigureWrapper(f)

    ax.plot(...
    plt.savefig(...
# .....

_wrapped_figureKapsam dışına çıktığında , çalışma zamanı __del__()yöntemimizi plt.close()içeride çağırır . _wrapped_figureOluşturucudan sonra istisna tetiklense bile olur .


0

Bu, yalnızca uyarıyı geçici olarak bastırmak istiyorsanız da yararlıdır:

    import matplotlib.pyplot as plt
       
    with plt.rc_context(rc={'figure.max_open_warning': 0}):
        lots_of_plots()
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.