HOWTO · Matplotlib
Matplotlib の cla()、clf()、close() の違い
matplotlib.pyplot.cla() は現在の Axes をクリアし、matplotlib.pyplot.clf() は現在の Figure をクリアします。matplotlib.pyplot.close() は Figure を閉じます。それぞれが影響する範囲と使い分けを、例とともに説明します。
このページの内容
どのオブジェクトに作用させたいかで関数を選んでください。matplotlib.pyplot.cla() は現在の Axes をクリアし、matplotlib.pyplot.clf() は現在の Figure をクリアし、matplotlib.pyplot.close() は Figure を閉じます。これらはいずれも pyplot の関数であり、メソッドではありません。また、それぞれ作用する範囲が異なります。サブプロットを 1 つだけ描き直すなら cla()、Figure を新しいレイアウトで再利用するなら clf()、Figure を使い終えたなら close() を使います。
違いの概要
この 3 つの関数は、作用範囲の階層をなしています。cla() は最小単位である 1 つの Axes に作用します。clf() はその Axes を含む Figure 全体に作用します。close() はさらに一歩進んで、Figure を pyplot から完全に取り除きます。
| 関数 | 作用対象 | 削除されるもの | 残るもの |
|---|---|---|---|
plt.cla() |
現在の Axes | 描画データ、ラベル、タイトル、凡例、その他の Axes の内容 | Axes 自体、レイアウト内の位置、隣接するすべての Axes、Figure |
plt.clf() |
現在の Figure | Figure 内のすべての Axes とその内容 | Figure オブジェクトとそのウィンドウ(新しい Axes を受け入れる準備ができている) |
plt.close() |
pyplot が管理する Figure | pyplot への Figure の登録(対話型バックエンドではそのウィンドウも) | その Figure については pyplot 内に何も残らない。隣接する Figure は影響を受けない |
作用範囲が最も重要になるのは、Figure に複数のサブプロットが含まれている場合です。Axes をクリアすると、その Axes はレイアウト内に残るため、その位置と他のサブプロットは引き続き利用できます。Figure をクリアすると、そのすべての Axes が一度に削除されますが、Figure 自体は新しい Axes やプロットのために利用可能なまま残ります。Figure を閉じると pyplot によるその Figure の管理が終了します。これは 3 つのうちで唯一、Figure のリソースを解放する関数です。
3 つの pyplot 関数はすべて、事前に対象を選択しない限り、現在の Axes または Figure に作用します。Axes オブジェクトや Figure オブジェクトをすでに保持している場合は、オブジェクトレベルの操作 ax.clear() と fig.clear() を使うことで、どの Axes や Figure が現在のものであるかに頼らず、対象を明示できます。close() については、plt.close(fig) のように Figure を直接渡すことができます。
matplotlib.pyplot.cla()
cla() は、現在の Axes の描画データ、軸ラベル、タイトル、凡例、その他の内容をクリアします。Axes 自体は Figure 内の元の位置に残るため、サブプロットのグリッドはレイアウトを保持し、隣接する各サブプロットはそのプロットを保持します。Axes をクリアしても、それが fig.axes から削除されることはなく、隣接要素の位置が変わることもありません。
このため cla() は更新ループに最適な道具です。Axes を選択し、その内容をクリアし、新しいデータを同じ場所に描画します。これは、Figure の残りの部分には手を触れずに 1 つのパネルだけを更新するシンプルなアニメーションやダッシュボードの背後にあるパターンです。
次の例では、2 つのサブプロットを作成し、そのうち 2 番目だけをクリアします。出力される確認結果は、Figure に両方の Axes がまだ含まれていること、最初の Axes には線が残っていること、2 番目の Axes には線がなくタイトルが空であることを示しています。
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(0, 2 * np.pi, 100)
fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(8, 3.2))
ax1.plot(x, np.sin(x))
ax1.set_title("Sine (kept)")
ax2.plot(x, np.cos(x), color="orange")
ax2.set_title("Cosine (to be cleared)")
fig.tight_layout()
plt.sca(ax2)
plt.cla()
print(f"axes={len(fig.axes)}; ax1_lines={len(ax1.lines)}; ax2_lines={len(ax2.lines)}; ax2_title={ax2.get_title()!r}")
plt.close(fig)
出力:
axes=2; ax1_lines=1; ax2_lines=0; ax2_title=''
呼び出し前は、両方のサブプロットに曲線が描かれています。
plt.cla() の後は、2 番目の Axes は空になりますが、その場所を引き続き占有し、最初のサブプロットは変化していません。
サブプロットの配置を維持したまま 1 つの Axes の内容だけを更新したい場合は、cla() を選んでください。コードに Axes 変数がすでにある場合は、ax.clear() が、pyplot の現在の Axes の選択を変更することなく、同じ Axes レベルの意図を表します。
matplotlib.pyplot.clf()
matplotlib.pyplot.clf() は、現在の Figure を、そのすべての Axes とその内容を含めてクリアします。Figure オブジェクトは再利用可能なまま残り、関連する Figure マネージャーは閉じられません。そのため、同じ Figure を(対話型セッションでは同じウィンドウを)ゼロから再び埋めることができます。Axes 単位の clf() 操作は存在しません。この関数は Figure の範囲で動作するためです。
clf() の後、Figure には Axes がまったく存在しないため、再び描画する前に新しい Axes を作成する必要があります。単なる plt.plot() の呼び出しであれば、新しい Axes が暗黙的に作成されます。複数の Figure が開いている場合は、plt.clf() を呼び出す前に意図したものを選択してください。fig.clear() はオブジェクトレベルの等価な操作であり、対象を明示します。
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig, axes = plt.subplots(1, 2, figsize=(8, 3.2))
axes[0].plot([0, 1, 2], [0, 1, 4])
axes[0].set_title("Line")
axes[1].bar(["A", "B", "C"], [3, 7, 5])
axes[1].set_title("Bar")
fig.tight_layout()
plt.figure(fig.number)
plt.clf()
print(f"figure_axes={len(fig.axes)}; figure_open={plt.fignum_exists(fig.number)}")
ax = fig.add_subplot(1, 1, 1)
ax.plot([0, 1, 2], [4, 1, 0], color="green")
ax.set_title("Rebuilt on the same Figure")
fig.tight_layout()
plt.close(fig)
出力:
figure_axes=0; figure_open=True
Figure は最初、折れ線グラフと棒グラフを左右に並べて保持しています。
出力は、clf() がすべての Axes を削除し(figure_axes=0)、Figure 自体は開いたままであったこと(figure_open=True)を確認しています。続いてこの例では、新しい Axes を 1 つ追加し、同じ Figure 上にまったく異なる単一のプロットを描画します。
Figure を利用可能なまま保持しつつ、そのすべてのプロットやサブプロットのレイアウトをゼロから再構築したい場合は、clf() を使用してください。同じ Figure 内の他の Axes に手を触れたくない場合は、代わりに cla() を使用してください。
matplotlib.pyplot.close()
matplotlib.pyplot.close() は、pyplot が管理する Figure を閉じます。引数なしで呼び出すと、現在の Figure が閉じられます。特定の Figure を対象にするには、Figure オブジェクト、その番号、または登録された Figure 名を渡します(例:plt.close(fig))。また、'all' を渡すとすべての Figure を一度に閉じることができます。
閉じると、その Figure は pyplot のレジストリから削除されるため、plt.fignum_exists() はそれを報告しなくなり、pyplot はその内容をメモリに保持しなくなります。クリアとの重要な違いはここにあります。close() の後は pyplot に再利用できる Figure は残りませんが、clf() の後は同じ Figure が空の状態で新しい内容を待っています。対話型バックエンドでは Figure のウィンドウも破棄されますが、目に見えるウィンドウの正確な動作はバックエンドと実行コンテキストに依存します。
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
fig1, ax1 = plt.subplots(figsize=(5, 3.2))
ax1.plot([0, 1, 2], [0, 2, 1])
ax1.set_title("Report 1 (to be closed)")
fig1.tight_layout()
fig2, ax2 = plt.subplots(figsize=(5, 3.2))
ax2.plot([0, 1, 2], [1, 0, 3], color="purple")
ax2.set_title("Report 2 (kept open)")
fig2.tight_layout()
print(f"open_before={plt.get_fignums()}")
plt.close(fig1)
print(f"open_after={plt.get_fignums()}; fig1_open={plt.fignum_exists(fig1.number)}")
plt.close("all")
出力:
open_before=[1, 2]
open_after=[2]; fig1_open=False
呼び出し後に登録されたまま残るのは Figure 2 だけであり、これが表示や保存のために利用可能なまま残る Figure です。
不要になった Figure を作成したときは、特に多くの Figure を生成するスクリプトやループでは、常に close() を使用してください。同じ Figure を再利用する必要がある場合は、代わりに clf() でクリアしてください。サブプロットを 1 つだけ更新する必要がある場合は、cla() または ax.clear() を使用してください。
メモリと Figure のライフサイクル
この 3 つの関数はコストの面でも異なります。cla() と clf() は安価で繰り返し可能な操作です。Figure は pyplot に登録されたままで、そのウィンドウとバッファは存続し、破棄されるのは内容だけです。これは、再び描画される Figure に対してまさに求められる動作です。close() は Figure 自体を解放する唯一の関数であり、そのため、二度と描画されない Figure に対する唯一の正しい締めくくりとなります。
pyplot は、作成したすべての Figure を、明示的に閉じられるまで登録し続け、そのレジストリが大きくなりすぎると警告を発します。ループ内で Figure を閉じずに作成し続けるとメモリが蓄積し、最終的に RuntimeWarning が発生します。
import warnings
import matplotlib
matplotlib.use("Agg")
import matplotlib.pyplot as plt
with warnings.catch_warnings(record=True) as caught:
warnings.simplefilter("always")
for _ in range(25):
plt.figure()
print(f"figures_open={len(plt.get_fignums())}")
print(f"too_many_figures_warning={any(str(w.message).startswith('More than 20 figures') for w in caught)}")
plt.close("all")
print(f"after_close_all={plt.get_fignums()}")
出力:
figures_open=25
too_many_figures_warning=True
after_close_all=[]
このループでは cla() も clf() も助けにはなりません。問題は内容ではなく、登録された Figure の数だからです。経験則は次のとおりです。同じ Figure が存続する場合はクリアし、存続しない場合は閉じる。Figure ごとに 1 ファイルを保存するバッチジョブは、保存直後に plt.close(fig) を呼び出すべきです。そうすれば、ジョブが生成するレポートの数にかかわらず、ピークメモリは一定に保たれます。
どの関数を使うべきか
- アニメーションやライブダッシュボードのように、複数パネルの Figure の 1 パネルを描き直す場合:レイアウトと他のパネルが存続するよう、
cla()(またはax.clear())を使用します。 - 同じウィンドウや Figure オブジェクトを維持したまま、異なるレイアウトで Figure を最初からやり直す場合:
clf()(またはfig.clear())を使用し、それから新しい Axes を追加します。 - Figure を使い終えた場合、特にループやバッチスクリプトの中では:pyplot がそれを解放するよう、Figure、その番号、または
'all'を渡してclose()を使用します。
最もよくある間違いは、cla() を意図した場所で clf() を使うこと(現在のものだけでなく Figure 内のすべてのサブプロットが静かに消去されます)、間違った Axes が現在の状態で cla() を呼び出すこと、そして閉じた Figure がその後も pyplot 経由で到達可能だと期待することです。要するに、1 つの Axes は cla() でクリアし、Figure 内のすべての Axes は clf() でクリアし、Figure は close() で締めくくります。