web-dev-qa-db-ja.com

開いている人物が多すぎるという警告

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

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

114
andreas-h

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

pyplotをインポートしたと仮定します

import matplotlib.pyplot as plt

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

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

plt.close()はウィンドウを閉じます 。これは、特に指定しない限り、現在のウィンドウになります。 plt.close('all')は、開いているすべての図形を閉じます。

del figが機能しない理由は、pyplot状態マシンが周囲の図への参照を保持しているためです(「現在の図」が何であるかを知る場合に必要です)。つまり、図のyourrefを削除しても、少なくとも1つのライブrefが存在するため、ガベージコレクションは行われません。

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

148
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%遅くなりました)。

20
Don Kirkby

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

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

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

10
mightypile