Matplotlib基礎--在 matplotlib 中建立三維圖表
Matplotlib 最開始被設計為僅支援二維的圖表。到 1.0 版本釋出左右,一些三維圖表的工具在二維展示的基礎上被建立了出來,結果就是 Matplotlib 提供了一個方便的(同時也是有限的)的可用於三維資料視覺化的一套工具。三維圖表可以使用載入mplot3d
工具包來啟用,這個包會隨著 Matplotlib 自動安裝:
from mpl_toolkits import mplot3d
一旦模組被匯入,三維 axes 就可以像其他普通 axes 一樣通過關鍵字引數projection='3d'
來建立:
import numpy as np import matplotlib.pyplotas plt
fig = plt.figure() ax = plt.axes(projection='3d')
plt.show()
三維 axes 啟用後,我們可以在上面繪製不同的三維圖表型別。三維圖表在 notebook 中使用互動式圖表展示會優於使用靜態展示;回憶我們前面介紹過,你可以使用%matplotlib notebook
而不是%matplotlib inline
來啟用互動式展示模式。
三維的點和線
三維圖表中最基礎的是使用(x, y, z)座標定義的一根線或散點的集合。前面介紹過普通的二維圖表,作為類比,使用ax.plot3D
和ax.scatter3D
函式可以建立三維折線和散點圖。這兩個函式的簽名與二維的版本基本一致,你可以參考[簡單折線圖]和[簡單散點圖]來複習一下這部分的內容。下面我們繪製一個三維中的三角螺旋,線上的附近在繪製一些隨機的點:
ax = plt.axes(projection='3d') # 三維螺旋線的資料 zline = np.linspace(0, 15, 1000) xline = np.sin(zline) yline = np.cos(zline) ax.plot3D(xline, yline, zline, 'gray') # 三維散點的資料 zdata = 15 * np.random.random(100) xdata = np.sin(zdata) + 0.1 * np.random.randn(100) ydata = np.cos(zdata) + 0.1 * np.random.randn(100) ax.scatter3D(xdata, ydata, zdata, c=zdata, cmap='Greens');
注意預設情況下,圖中的散點會有透明度的區別,用於體現在圖中散點的深度。雖然三維效果在靜態影象中難以顯示,你可以使用互動式的檢視來獲得更佳的三維直觀效果。
三維輪廓圖
類似於我們在[密度和輪廓圖]中介紹的內容,mplot3d
也包含著能夠建立三維浮雕影象的工具。就像二維的ax.contour
圖表,ax.contour3D
要求輸入資料的格式是二維普通網格上計算得到的 Z 軸的資料值。下面我們展示一個三維的正弦函式輪廓圖:
def f(x, y): return np.sin(np.sqrt(x ** 2 + y ** 2)) x = np.linspace(-6, 6, 30) y = np.linspace(-6, 6, 30) X, Y = np.meshgrid(x, y) Z = f(X, Y)
fig = plt.figure() ax = plt.axes(projection='3d') ax.contour3D(X, Y, Z, 50, cmap='binary') ax.set_xlabel('x') ax.set_ylabel('y') ax.set_zlabel('z');
有時候預設的視角角度不是最理想的,在這種情況下我們可以使用view_init
函式來設定水平角和方位角。在下面的例子中,我們使用的是 60° 的水平角(即以 60° 俯視 x-y 平面)和 35° 的方位角(即將 z 軸逆時針旋轉 35°):
ax.view_init(60, 35) fig
同樣,注意到當使用 Matplotlib 互動式展示是,這樣的旋轉可以通過滑鼠點選和拖拽來實現。
框線圖和表面圖
使用網格資料生成的三維圖表還有框線圖和表面圖。這兩種圖表將網格資料投射到特定的三維表面,能夠使得結果影象非常直觀和具有說服力。下面是一個框線圖的例子:
fig = plt.figure() ax = plt.axes(projection='3d') ax.plot_wireframe(X, Y, Z, color='black') ax.set_title('wireframe');
表面圖類似框線圖,區別在於每個框線構成的多邊形都使用顏色進行了填充。新增色圖用於填充多邊形能夠讓圖形表面展示出來:
ax = plt.axes(projection='3d') ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis', edgecolor='none') ax.set_title('surface');
注意雖然每個顏色填充的表面都是二維的,但是表面的邊緣不需要是直線構成的。下面的例子使用surface3D
繪製了一個部分極座標網格,能夠讓我們切入到函式內部觀察效果:
r = np.linspace(0, 6, 20) theta = np.linspace(-0.9 * np.pi, 0.8 * np.pi, 40) r, theta = np.meshgrid(r, theta) X = r * np.sin(theta) Y = r * np.cos(theta) Z = f(X, Y) ax = plt.axes(projection='3d') ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap='viridis', edgecolor='none');
表面三角剖分
在一些應用場合中,上面的這種均勻網格繪製的圖表方式太過於侷限和不方便。在這些情況下,三角剖分的圖表可以派上用場。如果我們並不是使用笛卡爾座標系或極座標系的網格來繪製三維圖表,而是使用一組隨機的點來繪製三維圖表呢?
theta = 2 * np.pi * np.random.random(1000) r = 6 * np.random.random(1000) x = np.ravel(r * np.sin(theta)) y = np.ravel(r * np.cos(theta)) z = f(x, y)
ax = plt.axes(projection='3d') ax.scatter(x, y, z, c=z, cmap='viridis', linewidth=0.5);
上圖並未形象的表示出表面情況。這種情況下我們可以使用ax.plot_trisurf
函式,它能首先根據我們的資料輸入找到各點內在的三角函式形式,然後繪製表面(注意的是這裡的 x,y,z 是一維的陣列):
ax = plt.axes(projection='3d') ax.plot_trisurf(x, y, z, cmap='viridis', edgecolor='none');
上圖的結果很顯然沒有使用網格繪製表面圖那麼清晰,但是對於我們並不是使用函式構建資料樣本(資料樣本通常來自真實世界的取樣)的情況下,這能提供很大的幫助。例如我們下面會看到,能使用這種方法繪製一條三維的莫比烏斯環。
例子:繪製莫比烏斯環
theta = np.linspace(0, 2 * np.pi, 30) w = np.linspace(-0.25, 0.25, 8) w, theta = np.meshgrid(w, theta)
phi = 0.5 * theta
現在我們已經有了所有需要獲得三維座標值的引數了。我們定義為每個座標點距離環形中間的位置,使用它來計算最終(x,y,z)三維座標系的座標值:
# r是座標點距離環形中心的距離值 r = 1 + w * np.cos(phi) # 利用簡單的三角函式知識算得x,y,z座標值 x = np.ravel(r * np.cos(theta)) y = np.ravel(r * np.sin(theta)) z = np.ravel(w * np.sin(phi))
最後,為了繪製物件,我們必須保證三角剖分是正確的。實現這個最好的方法是在底層的引數上面實現三角剖分,最後讓 Matplotlib 將這個三角剖分投射到三維空間中形成莫比烏斯環。下面的程式碼最終繪製圖形:
# 在底層引數的基礎上進行三角剖分 from matplotlib.tri import Triangulation tri = Triangulation(np.ravel(w), np.ravel(theta)) ax = plt.axes(projection='3d') ax.plot_trisurf(x, y, z, triangles=tri.triangles, cmap='viridis', linewidths=0.2); ax.set_xlim(-1, 1); ax.set_ylim(-1, 1); ax.set_zlim(-1, 1);
結合這些技巧,能夠為你提供在 Matplotlib 建立和展現大量三維物件和模式的能力。