開いている数字が多すぎるという警告


166

で多くの図を作成するスクリプトでfix, ax = plt.subplots(...)RuntimeWarning:20を超える図が開かれたという警告が表示されます。pyplotインターフェース(matplotlib.pyplot.figure)を介して作成された数値は、明示的に閉じられるまで保持され、メモリを大量に消費する可能性があります。

ただし、この警告が表示される理由はわかりません。Figureをで保存した後fig.savefig(...)、で削除したためfig.clear(); del figです。コードのどの時点でも、一度に複数のFigureを開いています。それでも、オープンフィギュアが多すぎるという警告が表示されます。それはどういう意味ですか/警告を受け取らないようにするにはどうすればよいですか?


9
これをたくさん行っていて、インタラクティブに何も表示しない場合は、plt完全にバイパスする方がよいでしょう。例えば、stackoverflow.com / a / 16337909/325565(自分の答えの1つを差し込むのではなく、私が最も早く見つけられるものです...)
Joe Kington 14

1
@JoeKingtonありがとうこれはより良い解決策です
hihell

Joe Kingtonの回答がメインの回答リストにあるはずです。これは機能し、plt.close()がDon Kirbyが述べたプログラムの速度を落とす問題にも対処します。
NatalieL

回答:


198

新しい Figure を作成する代わりに、Figureオブジェクトで.clfまたは.claを使用します。@DavidZwickerから

あなたがpyplotとしてインポートしたと仮定

import matplotlib.pyplot as plt

plt.cla()、つまり現在のFigureで現在アクティブな軸をクリアします。他の軸はそのままです。

plt.clf()現在のFigure全体とそのすべての軸をクリアしますが、ウィンドウを開いたままにして、他のプロットで再利用できるようにします。

plt.close()特に指定がない場合、現在のウィンドウであるウィンドウを閉じますplt.close('all')開いている図をすべて閉じます。

del fig機能しない理由は、pyplotステートマシンが周りの図への参照を保持しているためです(「現在の図」が何であるかを知るために必要なため)。削除しても、この手段あなたの数字にrefは、それゆえ、それはゴミが収集されることはありません、少なくとも一つのライブ参照があります。

私はこの答えのためにここで集合的な知恵を調査しているので、@ JoeKingtonはplt.close(fig)pylabステートマシン(plt._pylab_helpers.Gcf)から特定のFigureインスタンスを削除し、ガベージコレクションを許可するコメントを述べています。


1
んー クラスにはありますがclf、ありfigureませんclosedel figFigureを実際に閉じて削除しないのはなぜですか?
andreas-h 2014

2
@ andreas-h私の推測:独自のハンドラーを持つウィンドウマネージャーのような複雑なものでは、何かをスコープ外に置くよりもクリーンアップが必要になる可能性があります。closeFigureオブジェクトで機能しないあなたの権利はplt.close()、ではなくのように呼び出しますfig.clf()
2014

5
@ andreas-h-基本的に、del fig機能しない理由は、それに__del__メソッド(基本的にはを呼び出すplt.close(fig))を指定すると、この特定のケースでは循環参照が発生しfig__del__メソッドがあると他のものがガベージコレクションされなくなるためです。 。(とにかく、それは私の漠然とした記憶です。)とにかく、それは確かに少し迷惑ですが、plt.close(fig)ではなくを呼び出す必要がありますdel fig。余談ですが、matplotlibは実際にこのためにコンテキストマネージャーを使用できます...
Joe Kington 14

6
@Hooked-もう少し明確にplt.close(fig)するために、pylabステートマシン(plt._pylab_helpers.Gcf)から特定のFigureインスタンスを削除し、ガベージコレクションを許可するように質問を編集します。
Joe Kington、2014

2
@JoeKington pltは少し混乱しており、その多くをやり直す方法についての考えがあります。コンテキストマネージャは....を参照してください興味をそそられるgithub.com/matplotlib/matplotlib/pull/2736github.com/matplotlib/matplotlib/pull/2624
tacaswell

32

Hookedの回答をさらに詳しく説明します。その答えを最初に読んだとき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()

パフォーマンスを測定して、バッチ内で図を再利用する価値があるかどうかを確認しました。この小さなサンプルプログラムはclose()、すべてのプロットの後に呼び出したときに41秒から49秒(20%遅く)に遅くなりました。


これは素晴らしい答えです。受け入れられた回答は、実際の目前の問題、つまりメモリ消費に対処していません。
カイル

24

意図的に多くのプロットをメモリに保持するつもりでも、警告されたくない場合は、図を生成する前にオプションを更新できます。

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

これにより、メモリの管理方法を変更せずに警告が発せられなくなります。


Jupyter環境で、プロットを示すセルが存在する限り、メモリ割り当ては維持されますか?
マタンスター

2
@matanster、それ自体が質問なので、私はそれを投稿します。私は答え始めましたが、jupyterのカーネル管理について正直に答えるのに十分な知識が本当にないことに気付きました。
マイティパイル2018

@matansterユーザーがカーネルを明示的にシャットダウンするまで、それらに割り当てられたすべての変数とメモリが存在します。セルにはリンクされていません。新しいJupyter Hubでは、システムはカーネルをシャットダウンできます(構成可能)。
greatvovan

0

次のスニペットは私のために問題を解決しました:


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_figureスコープ外になるランタイムは、私たちの呼び出しを__del__()持つメソッドをplt.close()内部。_wrapped_figureコンストラクタの後に例外が発生しても発生します。


0

これは、警告を一時的に抑制したい場合にも役立ちます。

    import matplotlib.pyplot as plt
       
    with plt.rc_context(rc={'figure.max_open_warning': 0}):
        lots_of_plots()
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.