HOWTO · Matplotlib
Matplotlib 中 cla()、clf() 與 close() 函式的差異
本文說明如何依要處理的物件選擇 pyplot 函式: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 函式,不是方法,且各自的作用範圍不同。當你只想重繪一個子圖時用 cla(),想以全新版面配置複用一個 Figure 時用 clf(),當你用完一個 Figure 時用 close()。
差異一覽
這三個函式構成作用範圍的層級。cla() 作用於最小單位,即單一 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 | 該 Figure 在 pyplot 中的註冊(在互動式後端中還包括其視窗) | 該 Figure 在 pyplot 中不復存在;相鄰的 Figure 不受影響 |
當一個 Figure 包含多個子圖時,作用範圍最為關鍵。清除一個 Axes 會讓該 Axes 保留在版面配置中,因此它的位置和其他子圖仍然可用。清除一個 Figure 會一次性移除它的全部 Axes,但 Figure 本身仍可用於新的 Axes 和繪圖。關閉一個 Figure 則結束 pyplot 對它的管理,這是三者中唯一能釋放 Figure 資源的函式。
除非先選定目標,否則這三個 pyplot 函式都作用於目前的 Axes 或 Figure。如果你已經持有 Axes 或 Figure 物件,物件層級操作 ax.clear() 和 fig.clear() 可以明確指定目標,而不必依賴目前恰好是哪一個 Axes 或 Figure。對於 close(),你可以直接傳入 Figure,例如 plt.close(fig)。
matplotlib.pyplot.cla()
cla() 清除目前 Axes 的繪製資料、座標軸標籤、標題、圖例及其他內容。Axes 本身仍留在 Figure 中的原位置,因此子圖格線保持其版面配置,每個相鄰子圖也保留其圖形。清除一個 Axes 不會把它從 fig.axes 中移除,也不會改變其相鄰子圖的位置。
這使 cla() 成為更新迴圈的合適工具:選中該 Axes,清除其內容,再把新資料繪製到同一位置。簡單的動畫和儀表板正是採用這種模式——只刷新單一面板,而 Figure 的其餘部分保持不變。
下面的範例建立兩個子圖,然後只清除第二個。印出的檢查結果表明,Figure 仍然包含兩個 Axes,第一個 Axes 仍保留其曲線,第二個 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() 之後,第二個 Axes 變空,但仍佔據其位置,第一個子圖保持不變:
當子圖的排列應保持不變、只需刷新一個 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,並在同一個 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() 清除它;如果只需刷新一個子圖,則使用 cla() 或 ax.clear()。
記憶體與 Figure 的生命週期
這三個函式在代價上也有所不同。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 儲存一個檔案的批次處理工作,應在儲存後立即呼叫 plt.close(fig),這樣無論工作產生多少份報告,峰值記憶體都保持恆定。
應該使用哪個函式?
- 重繪多面板 Figure 中的一個面板,例如在動畫或即時儀表板中:使用
cla()(或ax.clear()),以便版面配置和其他面板得以保留。 - 以不同的版面配置重新開始一個 Figure,同時保持同一個視窗或 Figure 物件:使用
clf()(或fig.clear()),然後添加新的 Axes。 - 用完了一個 Figure,尤其是在迴圈或批次指令碼中:使用
close(),傳入 Figure、其編號或'all',以便 pyplot 釋放它。
最常見的錯誤包括:在本該用 cla() 的地方用了 clf()——這會悄無聲息地抹除 Figure 中的全部子圖,而不只是目前那一個;在錯誤的 Axes 處於目前狀態時呼叫 cla();以及期望一個已關閉的 Figure 之後仍能透過 pyplot 存取。簡而言之:用 cla() 清除一個 Axes,用 clf() 清除一個 Figure 中的全部 Axes,用 close() 為一個 Figure 收尾。