Matplotlib 立體圖


如果想將資料以立體方式呈現,可以透過 mpl_toolkits.mplot3d 模組,例如,來畫個立體螺旋圖:

import numpy as np
import matplotlib.pyplot as plt

start = 0
end = np.pi * 20   
step = np.pi / 180

x = np.arange(start, end, step)
y = np.sin(x)
z = np.cos(x) 

# 取得 mpl_toolkits.mplot3d.axes3d.Axes3D 實例
ax = plt.axes(projection='3d')
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.plot(x, y, z)
plt.title('Axes3D Plot')
plt.show()

透過 plt.axes(projection='3d') 可以取得 Axes3D 實例,另一個方式是自行建立:

import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D

fig = plt.gcf()
ax = Axes3D(fig)

如果只是要畫線圖,透過 Axes3D 實例的 plot 方法,指定 x、y、z 就可以了,這會顯示以下的圖形:

Matplotlib 立體圖

如果想繪製曲面圖,可以透過 plot_surface 方法,例如:

import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

def f(x, y):
    n = np.sqrt(np.power(x, 2) + np.power(y, 2)) / 180 * np.pi
    return np.cos(n) + np.cos(3 * n)

width = 200
step = 10

x = np.arange(-width, width, step)
y = np.arange(-width, width, step)

X, Y = np.meshgrid(x, y) 
Z = f(X, Y)

ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, cmap = cm.coolwarm) 
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_box_aspect((1, 1, 0.5))
plt.title('Axes3D Plot Surface')
plt.show()

plot_surface 預設並不分層著色,這邊指定了 cm.coolwarm,值高的部份會是代表熱度的紅色,值低的部份會是藍色,預設的圖形在三個軸是 1:1:1,也就是會以正立方體來繪製,這會讓這個函式圖的 z 軸顯得過於突冗,可以透過 set_box_aspect 來調整比例,執行後的圖形如下:

Matplotlib 立體圖

如果只畫線框圖,可以透過 plot_wireframe,例如,將範例中的 ax.plot_surface(X, Y, Z, cmap = cm.coolwarm) 改為 ax.plot_wireframe(X, Y, Z),會出現以下的圖形:

Matplotlib 立體圖

在〈Matplotlib 圖片、等值輪廓線〉談過,二維 Perlin 雜訊可以用來當成起伏的地形的計算基礎,來看看若將二維 Perlin 雜訊畫為曲面的話,會是什麼樣貌:

from math import floor
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

def blending(t):
    return 6 * (t ** 5) - 15 * (t ** 4) + 10 * (t ** 3)

def lerp(g1, g2, t):
    return g1 + t * (g2 - g1)

def grad2(hashvalue, dx, dy):
    return [dy, dx + dy, dx, dx - dy, -dy, -dx - dy, -dx, -dx + dy][hashvalue % 8];

rand_table = np.random.randint(255, size = 256).tolist()
def _perlin2(x, y):
    xi = floor(x)
    yi = floor(y)

    aa = rand_table[
        (rand_table[xi % 256] + yi) % 256
    ]
    ba = rand_table[
        (rand_table[(xi + 1) % 256] + yi) % 256
    ]
    ab = rand_table[
        (rand_table[xi % 256] + yi + 1) % 256
    ]
    bb = rand_table[
        (rand_table[(xi + 1) % 256] + yi + 1) % 256
    ]

    dx = x - xi
    dy = y - yi    

    u = blending(dx)
    v = blending(dy)

    g1 = lerp(grad2(aa, dx, dy), grad2(ba, dx - 1, dy), u)
    g2 = lerp(grad2(ab, dx, dy - 1), grad2(bb, dx - 1, dy - 1), u)

    return lerp(g1, g2, v)
_perlin2 = np.frompyfunc(_perlin2, 2, 1)

def perlin2(x, y):
    cx, cy = np.meshgrid(x, y)
    return _perlin2(cx, cy).astype(np.float)

width = 100
x = np.arange(width)
y = np.arange(width)

X, Y = np.meshgrid(x, y) 
Z = perlin2(x / 25, y / 25)

ax = plt.axes(projection='3d')
ax.plot_surface(X, Y, Z, cmap = cm.gist_earth) # 用地形高度顏色來著色
ax.set_xlabel('x')
ax.set_ylabel('y')
ax.set_zlabel('z')
ax.set_box_aspect((1, 1, 25 / width))
plt.title('Perlin noise')
plt.show()

結果會如下:

Matplotlib 立體圖

可以畫的圖案還有很多種,想知道有哪些以及怎麼畫,最好的方式就是搜尋一下「axes3d」,看看有沒有你想要的圖案效果,然後察看有沒有範例可以參考。