HOWTO · Matplotlib
如何在 Matplotlib 中建立曲面圖
我們可以使用 plot_surface()方法在 Matplotlib 中建立表面圖。
本頁內容
若要在 Matplotlib 中建立 3D 曲面圖,請先準備形狀相容的二維 X、Y 和 Z 陣列,建立 projection="3d" 座標軸,再將這些陣列傳給 Axes3D.plot_surface。下方範例會根據數學函數建立座標網格、標示座標軸、加入色彩條並儲存圖形。只要座標與高度值能表示在矩形網格上,同樣的流程也適用於測量資料。
設定環境
使用 NumPy 處理陣列與網格,並使用 Matplotlib 繪圖。在終端機中,於預計使用的 Python 環境執行 python -m pip install numpy matplotlib。如果你已透過環境管理工具或筆記本管理套件,請改在該處安裝這兩個套件,確保執行腳本的直譯器可以使用它們。
範例會匯入 NumPy 和 matplotlib.pyplot。Matplotlib 已內建 3D 繪圖功能;projection="3d" 會建立 3D 座標軸,因此不需要另外匯入 Axes3D。在該座標軸上使用的方法是 Axes3D.plot_surface。
準備資料
曲面圖會為二維座標平面上的每個位置指定一個高度。若座標間距固定,可用 np.linspace() 建立一維座標值,再用 np.meshgrid() 將它們展開為二維的 X 和 Y 網格。接著根據這些網格計算 Z,或提供排列在相同網格位置上的測量高度值。
三個陣列的形狀必須相容:每個 Z[i, j] 都是座標點 X[i, j]、Y[i, j] 對應的高度。目前的 Matplotlib 版本也會接受一維的 x、y 座標向量,並配合二維的 Z 將其展開;但高度陣列本身仍必須與網格維度一致:若 Z 是在與 X、Y 不同的網格上計算出來的,就會引發 ValueError,下方的疑難排解一節會以實際執行結果說明。請先建立座標網格,再在同樣的網格上計算或重塑高度陣列。若散佈測量點無法形成矩形網格,請先準備或內插成網格資料,再使用 plot_surface。
建立曲面圖
這個完整範例使用平滑的徑向函數 sin(sqrt(x² + y²))。它會建立網格、繪製曲面、標示三個維度、加入色彩刻度,並將結果儲存為目前目錄中的 surface-plot.png。範例中的座標軸、標題與色彩條可見標籤均維持英文。請在已安裝 NumPy 和 Matplotlib 的環境中執行。範例不會在主控台列印文字;執行結果是儲存的影像,以及在目前後端支援顯示時開啟的 Matplotlib 圖形視窗。
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
surface = ax.plot_surface(X, Y, Z, cmap="viridis")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Surface Plot")
fig.colorbar(surface, ax=ax, label="Z value")
fig.savefig("surface-plot.png", dpi=150, bbox_inches="tight")
plt.show()
由於此腳本不會列印文字,因此沒有主控台輸出區塊。色彩條用來對照曲面顏色與其數值;網格決定幾何形狀,色彩映射則控制外觀。在筆記本中,繪圖環境通常會顯示圖形;在腳本中,plt.show() 會要求已設定的 Matplotlib 後端顯示圖形。
自訂曲面圖
下方範例將最常見的自訂方式套用到同一個曲面。它將色彩映射切換為 plasma、移除網格邊線,並在加入色彩條、將圖形儲存為 customized-surface-plot.png 之前,將視角旋轉到固定的仰角與方位角。
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
surface = ax.plot_surface(X, Y, Z, cmap="plasma", edgecolor="none")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Customized Surface Plot")
ax.view_init(elev=30, azim=45)
fig.colorbar(surface, ax=ax, label="Z value")
fig.savefig("customized-surface-plot.png", dpi=150, bbox_inches="tight")
plt.show()
選擇能清楚呈現 Z 變化的色彩映射。在此範例中,cmap="plasma" 取代了第一個範例的 viridis 漸層,而色彩條可協助解讀這種對應關係。edgecolor="none" 選項會移除繪製出的網格邊線,這些邊線在密集網格上可能干擾連續曲面的呈現;若希望取樣網格本身保持可見,請保留預設邊線。
若要調整視角,請在儲存或顯示圖形前呼叫 ax.view_init(elev=30, azim=45)。elev 用來調整觀看高度,azim 則讓視角繞著垂直軸旋轉。請調整這些值,直到形狀與座標軸都清楚可見;合適的角度取決於資料及你想呈現的內容。與第一張圖相比,第二張圖旋轉後的視角能看到更多中央高峰的遠側斜面。
對於密集網格,plot_surface 可使用 rcount 和 ccount 等列與欄的取樣控制項。降低數量會繪製較少的取樣列與欄,可能加快大型曲面的繪製速度,但會犧牲可見細節。請保留足夠的取樣點,以呈現資料中的重要變化。使用 fig.savefig("surface-plot.png", dpi=150, bbox_inches="tight") 儲存可分享的影像;為了相容於顯示後會關閉圖形的後端,請在 plt.show() 前呼叫它。若要保留多個版本,請改用不同的檔名或目錄。
與線框圖比較
線框圖只繪製同一曲面的網格線,不填滿表面,也不使用色彩映射。Axes3D.plot_wireframe 接受相同的 X、Y、Z 網格,因此你不需要準備新資料,就能在兩種檢視之間切換。當網格結構本身很重要、填滿的曲面會遮住形狀的遠側,或需要在正式繪製曲面前快速檢查陌生資料的結構時,線框圖很有用。
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 50)
y = np.linspace(-5, 5, 50)
X, Y = np.meshgrid(x, y)
Z = np.sin(np.sqrt(X**2 + Y**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.plot_wireframe(X, Y, Z, rstride=2, cstride=2, color="tab:blue")
ax.set_xlabel("X")
ax.set_ylabel("Y")
ax.set_zlabel("Z")
ax.set_title("Wireframe Plot")
fig.savefig("wireframe-plot.png", dpi=150, bbox_inches="tight")
plt.show()
rstride 和 cstride 引數會跳過部分列與欄,讓線條保持易讀;範例會繪製 50×50 網格中每隔一列與一欄的線。與 plot_surface 不同,線框圖沒有表面顏色,因此沒有需要色彩條說明的對象。對於最終呈現,填滿的曲面通常能更好地傳達高度差異,而線框圖則更清楚地顯示網格本身的取樣方式。
修正形狀不相容的錯誤
plot_surface 最常見的失敗,是 Z 陣列的形狀與座標網格不一致。下方腳本在 50×50 的網格上計算 Z,卻將它與 100×100 的 X、Y 網格一起傳入:
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(-5, 5, 100)
y = np.linspace(-5, 5, 100)
X, Y = np.meshgrid(x, y)
x_small = np.linspace(-5, 5, 50)
y_small = np.linspace(-5, 5, 50)
X_small, Y_small = np.meshgrid(x_small, y_small)
Z = np.sin(np.sqrt(X_small**2 + Y_small**2))
fig = plt.figure()
ax = fig.add_subplot(111, projection="3d")
ax.plot_surface(X, Y, Z, cmap="viridis")
plt.show()
執行腳本時,會在 plot_surface 的呼叫處停止,並出現這個錯誤:
ValueError: shape mismatch: objects cannot be broadcast to a single shape. Mismatch is between arg 0 with shape (100, 100) and arg 2 with shape (50, 50).
修正方法是依照第一個範例的做法,從傳給 plot_surface 的同一個網格計算 Z,讓 Z.shape 等於 X.shape 和 Y.shape。若不確定,請在繪圖前印出三個形狀並比較:Z 的每個元素都需要恰好一組座標,不能多也不能少。
結論
主要步驟是建立座標網格、讓 Z 的形狀與網格相符、加入 3D 座標軸,並呼叫 Axes3D.plot_surface。色彩映射與色彩條能說明高度值,視角和取樣控制項則有助於清楚呈現形狀。當你需要檢查網格本身時,plot_wireframe 會以線條繪製相同的資料。需要影像檔時請儲存圖形,並依照要繪製的資料調整網格解析度或視角。
常見問題
-
曲面圖可以使用哪些資料?
使用由兩個座標定義的數值,且每個高度都對應矩形網格中的一個位置。函數適合用於示範;測量值則需排列在形狀相容的座標網格上。散佈點必須先轉換為網格資料。 -
如何安裝所需的程式庫?
請在執行腳本的環境中使用python -m pip install numpy matplotlib安裝 NumPy 和 Matplotlib。 -
可以更改曲面顏色嗎?
可以。設定plot_surface的cmap引數,再使用fig.colorbar(surface, ax=ax)加入色彩條,顯示顏色與數值的對應關係。 -
如何調整視角?
使用ax.view_init(elev=..., azim=...)。仰角會改變相機高度,方位角則會改變相機繞著圖形旋轉的角度。 -
為什麼
X、Y和Z必須具有相容的形狀?
Matplotlib 需要為網格中的每個位置提供一組座標和一個高度。若Z是在不同的網格上計算出來的,plot_surface就會引發關於無法一起廣播之形狀的ValueError。請用np.meshgrid()建立座標網格,並根據同樣的網格計算Z,讓對應位置的元素彼此一致。