前言
制作 3D 圖形的 API 與 2D API 非常相似,我們已經學習了一系列2D統計圖的繪制,而在統計圖中再添加一個次元可以展示更多資訊。而且,在進行正常彙報或演講時,3D 圖形也可以吸引更多的注意力。在本文中,我們将探讨利用 Matplotlib 繪制三維統計圖。
3D散點圖
3D 散點圖的繪制方式與 2D 散點圖基本相同。
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
# 資料生成
a, b, c = 10., 28., 8. / 3.
def lorenz_map(x, dt = 1e-2):
x_dt = np.array([a * (x[1] - x[0]), x[0] * (b - x[2]) - x[1], x[0] * x[1] - c * x[2]])
return x + dt * x_dt
points = np.zeros((2000, 3))
x = np.array([.1, .0, .0])
for i in range(points.shape[0]):
points[i], x = x, lorenz_map(x)
# 繪制
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.set_xlabel('X axis')
ax.set_ylabel('Y axis')
ax.set_zlabel('Z axis')
ax.set_title('Lorenz Attractor a=%0.2f b=%0.2f c=%0.2f' % (a, b, c))
ax.scatter(points[:, 0], points[:, 1],points[:, 2], zdir = 'z', c = 'c')
plt.show()

Tips:按住滑鼠左鍵移動滑鼠可以旋轉檢視三維圖形将旋轉。
為了使用 Matplotlib 進行三維操作,我們首先需要導入 Matplotlib 的三維擴充:
from mpl_toolkits.mplot3d import Axes3D
對于三維繪圖,需要建立一個 Figure 執行個體并附加一個 Axes3D 執行個體:
fig = plt.figure()
ax = fig.gca(projection='3d')
之後,三維散點圖的繪制方式與二維散點圖完全相同:
ax.scatter(points[:, 0], points[:, 1],points[:, 2], zdir = 'z', c = 'c')
Tips:需要調用 Axes3D 執行個體的 scatter() 方法,而非 plt 中的 scatter 方法。隻有 Axes3D 中的 scatter() 方法才能解釋三維資料。同時 2D 統計圖中的注釋也可以在 3D 圖中使用,例如 set_title()、set_xlabel()、set_ylabel() 和 set_zlabel() 等。
同時可以通過使用 Axes3D.scatter() 的可選參數更改統計通的形狀和顔色:
ax.scatter(points[:, 0], points[:, 1],points[:, 2], zdir = 'z', c = 'c', marker='s', edgecolor='0.5', facecolor='m')
3D曲線圖
與在 3D 空間中繪制散點圖類似,繪制 3D 曲線圖同樣需要設定一個 Axes3D 執行個體,然後調用其 plot() 方法:
# 構造資料集
a, b, c = 10., 28., 8. / 3.
def lorenz_map(x, dt = 1e-2):
x_dt = np.array([a * (x[1] - x[0]), x[0] * (b - x[2]) - x[1], x[0] * x[1] - c * x[2]])
return x + dt * x_dt
points = np.zeros((8000, 3))
x = np.array([.1, .0, .0])
for i in range(points.shape[0]):
points[i], x = x, lorenz_map(x)
# Plotting
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.plot(points[:, 0], points[:, 1], points[:, 2], c = 'c')
plt.show()
3D标量場
到目前為止,我們看到的 3D 繪圖方式類似與相應的 2D 繪圖方式,但也有許多特有的三維繪圖功能,例如将二維标量場繪制為 3D 曲面:
x = np.linspace(-3, 3, 256)
y = np.linspace(-3, 3, 256)
x_grid, y_grid = np.meshgrid(x, y)
z = np.sinc(np.sqrt(x_grid ** 2 + y_grid ** 2))
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.plot_surface(x_grid, y_grid, z, cmap=cm.viridis)
plt.show()
Tips: plot_surface() 方法使用 x、y 和 z 将标量場顯示為三維曲面。
可以看到曲面上線條帶有顯著色彩,如果不希望看到三維曲面上顯示的曲線色彩,可以使用 plot_surface() 附加可選參數:
ax.plot_surface(x_grid, y_grid, z, cmap=cm.viridis, linewidth=0, antialiased=False)
同樣,我們也可以僅保持曲線色彩,而曲面不使用其他顔色,這也可以通過 plot_surface() 的可選參數來完成:
x = np.linspace(-3, 3, 256)
y = np.linspace(-3, 3, 256)
x_grid, y_grid = np.meshgrid(x, y)
z = np.sinc(np.sqrt(x_grid ** 2 + y_grid ** 2))
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.plot_surface(x_grid, y_grid, z, edgecolor='b',color='w')
plt.show()
而如果我們希望消除曲面,而僅使用線框進行繪制,這可以使用 plot_wireframe() 函數:
ax.plot_wireframe(x_grid, y_grid, z, cstride=10, rstride=10,color='c')
Tips:plot_wireframe() 參數與 plot_surface() 相同,使用兩個可選參數 rstride 和 cstride 用于令 Matplotlib 跳過x和y軸上指定數量的坐标,用于減少曲線的密度。
繪制3D曲面
在前述方法中,使用 plot_surface() 來繪制标量:即 f(x, y)=z 形式的函數,但 Matplotlib 也能夠使用更通用的方式繪制三維曲面:
# 資料生成
angle = np.linspace(0, 2 * np.pi, 32)
theta, phi = np.meshgrid(angle, angle)
r, r_w = .25, 1.
x = (r_w + r * np.cos(phi)) * np.cos(theta)
y = (r_w + r * np.cos(phi)) * np.sin(theta)
z = r * np.sin(phi)
# 繪制
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.set_xlim3d(-1, 1)
ax.set_ylim3d(-1, 1)
ax.set_zlim3d(-1, 1)
ax.plot_surface(x, y, z, color = 'c', edgecolor='m', rstride = 2, cstride = 2)
plt.show()
同樣可以使用 plot_wireframe() 替換對 plot_surface() 的調用,以便獲得圓環的線框視圖:
ax.plot_wireframe(x, y, z, edgecolor='c', rstride = 2, cstride = 1)
在3D坐标軸中繪制2D圖形
注釋三維圖形的一種有效方法是使用二維圖形:
x = np.linspace(-3, 3, 256)
y = np.linspace(-3, 3, 256)
x_grid, y_grid = np.meshgrid(x, y)
z = np.exp(-(x_grid ** 2 + y_grid ** 2))
u = np.exp(-(x ** 2))
fig = plt.figure()
ax = fig.gca(projection = '3d')
ax.set_zlim3d(0, 3)
ax.plot(x, u, zs=3, zdir='y', lw = 2, color = 'm')
ax.plot(x, u, zs=-3, zdir='x', lw = 2., color = 'c')
ax.plot_surface(x_grid, y_grid, z, color = 'b')
plt.show()
Axes3D 執行個體同樣支援常用的二維渲染指令,如 plot():
ax.plot(x, u, zs=3, zdir='y', lw = 2, color = 'm')
Axes3D 執行個體對 plot() 的調用有兩個新的可選參數:
- zdir:用于決定在哪個平面上繪制2D繪圖,可選值包括 x、y 或 z ;
- zs:用于決定平面的偏移。
是以,要将二維圖形嵌入到三維圖形中,隻需将二維原語用于 Axes3D 執行個體,同時使用可選參數,zdir 和 zs,來放置所需渲染圖形平面。
接下來,讓我們實際檢視下在 3D 空間中堆疊 2D 條形圖的示例:
import numpy as np
from matplotlib import cm
import matplotlib.colors as col
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
# 資料生成
alpha = 1. / np.linspace(1, 8, 5)
t = np.linspace(0, 5, 16)
t_grid, a_grid = np.meshgrid(t, alpha)
data = np.exp(-t_grid * a_grid)
# 繪制
fig = plt.figure()
ax = fig.gca(projection = '3d')
cmap = cm.ScalarMappable(col.Normalize(0, len(alpha)), cm.viridis)
for i, row in enumerate(data):
ax.bar(4 * t, row, zs=i, zdir='y', alpha=0.8, color=cmap.to_rgba(i))
plt.show()