Pynote

Python、機械学習、画像処理について

matplotlib - plot_surface で 3D グラフを描画する方法

基本的な使い方

Axes3D.plot_surface(X, Y, Z, *args, **kwargs) 

まず numpy.mgrid で格子状の点を作成し、この各点における関数の値を計算する。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-3:3, -3:3]
print("X:\n", X)
print("Y:\n", Y)

Z = f(X, Y)
print("Z:\n", Z)
X:
 [[-3 -3 -3 -3 -3 -3]
 [-2 -2 -2 -2 -2 -2]
 [-1 -1 -1 -1 -1 -1]
 [ 0  0  0  0  0  0]
 [ 1  1  1  1  1  1]
 [ 2  2  2  2  2  2]]
Y:
 [[-3 -2 -1  0  1  2]
 [-3 -2 -1  0  1  2]
 [-3 -2 -1  0  1  2]
 [-3 -2 -1  0  1  2]
 [-3 -2 -1  0  1  2]
 [-3 -2 -1  0  1  2]]
Z:
 [[27 19 13  9  7  7]
 [19 12  7  4  3  4]
 [13  7  3  1  1  3]
 [ 9  4  1  0  1  4]
 [ 7  3  1  1  3  7]
 [ 7  4  3  4  7 12]]

Axes 上に 3D グラフを追加するには、用意した X, Y, Z を plot_surface(X, Y, Z) と渡す。
3D グラフは四角形のポリゴン (patch) の集合 (Poly3DCollection) で構成される。
plot_surface() は返り値として Axes に追加したこのオブジェクトを返す。

fig = plt.figure(figsize=(9, 9), facecolor="w")
ax = fig.add_subplot(111, projection="3d")

surf = ax.plot_surface(X, Y, Z)
print(type(surf))  # <class 'mpl_toolkits.mplot3d.art3d.Poly3DCollection'>

plt.show()


影を無効にする。

shade 引数で影を付けるかどうかを設定できる。
デフォルトは有効になっている。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

fig = plt.figure(figsize=(10, 5))
ax1 = fig.add_subplot(121, projection="3d", facecolor="w")
ax1.set_title("shade=True")
ax1.plot_surface(X, Y, Z, shade=True)  # 影を有効

ax2 = fig.add_subplot(122, projection="3d", facecolor="w")
ax2.set_title("shade=False")
ax2.plot_surface(X, Y, Z, shade=False)  # 影を無効

plt.show()


パッチの間隔を変更する。

パッチは rstride, cstride 引数で指定した大きさで作成される。
デフォルトでは rstirde=1, cstride=1 のため、パッチは (1, 1) の大きさである。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection="3d", facecolor="w")
# (3, 3) の大きさでパッチを作成する。
ax.plot_surface(X, Y, Z, cmap="plasma", rstride=3, cstride=3)

plt.show()


パッチの境界線をなくす。

antialiased=False とすることで、パッチの境界線を無効にできる。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection="3d", facecolor="w")
# パッチの境界線を無効化
ax.plot_surface(X, Y, Z, antialiased=False)

plt.show()


パッチの境界線の色を変更する。

edgecolor 引数でパッチの境界線の色を設定できる。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

fig = plt.figure(figsize=(9, 9))
ax = fig.add_subplot(111, projection="3d", facecolor="w")
ax.plot_surface(X, Y, Z, cmap="plasma", edgecolor="w")

plt.show()


色を指定する。

color 引数で色を指定できる。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

fig = plt.figure(figsize=(9, 9))
ax = fig.add_subplot(111, projection="3d", facecolor="w")
ax.plot_surface(X, Y, Z, color="green")

plt.show()


カラーマップを指定する。

cmap 引数で値に応じて色を変化させるカラーマップを設定できる。

連続的な値の変化に対応した Sequential カテゴリのカラーマップがおすすめ。

http://pynote.hatenablog.com/entry/matplotlib-colorpynote.hatenablog.com

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection="3d", facecolor="w")
ax.plot_surface(X, Y, Z, cmap="plasma")

plt.show()


カラーバーを追加する。

plot_surface() の返り値 Poly3DCollection を Figure.colorbar() に渡すことで、カラーバーを Axes に追加できる。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

# 点でグラフを作成する。
fig = plt.figure(figsize=(9, 7))
ax = fig.add_subplot(111, projection="3d")
surf = ax.plot_surface(X, Y, Z, cmap="plasma", facecolor="w")
# カラーバーを追加する。
fig.colorbar(surf)

plt.show()


色を透過する。

alpha 引数に [0, 1] の範囲の float を指定することで透過度を設定できる。
デフォルトは 1.0 なので、透過なし。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection="3d", facecolor="w")
ax.plot_surface(X, Y, Z, edgecolor="black", alpha=0.3)

plt.show()


パッチごとに色を設定する。

以下の例では、格子が (20, 20) で rstride, cstride はデフォルトの (1, 1) であるから、パッチは (20, 20) 作られる。
facecolors に各パッチごとの色を指定した (20, 20, 3) の配列を渡すことで、パッチが指定した色で作成される。
色の値は [0, 255] ではなく、[0, 1] の範囲の浮動小数点数で指定することに注意する。

import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.mplot3d import Axes3D


def f(x, y):
    return x ** 2 + y ** 2 + x * y


X, Y = np.mgrid[-10:10, -10:10]
Z = f(X, Y)

# 各パッチの色を作成する。
colors = np.random.random(X.shape + (3,))
print(colors.shape)  # (20, 20, 3)

fig = plt.figure(figsize=(7, 7))
ax = fig.add_subplot(111, projection="3d", facecolor="w")
ax.plot_surface(X, Y, Z, facecolors=colors)

plt.show()