matplotlibによる3Dグラフ描画
この記事の目的
matplotlibを用いて3Dグラフを描画する方法についてまとめる
matplotlibを用いた3D線グラフ・散布図の描画
matplotlibで3Dグラフを描画する場合は下記のように実装する。
- (1) 3D描画用モジュールのインポート
matplotlibのインポート時に3次元描画に関するライブラリ(mpl_toolkitsのAxes3D)をインポートする。
import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D
- (2) 3D軸設定
描画する軸の設定時引数projection='3d'を付与することで3次元軸を追加する。
ax = fig.add_subplot(1,1,1,projection='3d') #subplotの追加 (行/列/描画対象インデックス,3次元軸の追加)
- (3) plot,scatter関数を3次元データを引数として呼び出す・
描画用関数(線グラフであればplot、散布図であればscatter)を呼び出す時にxyzの各座標を引数として呼び出す。 その際、xyzのデータ数は一致させる必要がある。
# xy平面にsin関数を描画する xs = np.linspace(-np.pi, np.pi, 100) ys = np.sin(xs) ax.plot(xs, ys, zs=0, zdir='z', label='sin (x, y)')
上記を組み合わせて3次元軸にsin・cosを描画するサンプルコードを下記に示す。
import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D fig = plt.figure() ax = fig.add_subplot(1,1,1,projection='3d') #subplotの追加 (行/列/描画対象インデックス,3次元軸の追加) # xy平面にsin関数を描画する xs = np.linspace(-np.pi, np.pi, 100) ys = np.sin(xs) ax.plot(xs, ys, zs=0, zdir='z', label='sin (x, y)') # xz平面にcos関数を描画する xs = np.linspace(-np.pi, np.pi, 100) ys= np.zeros([100]) zs = np.cos(xs) ax.scatter(xs, ys, zs, zdir='z', label='cos (x, y)',color="red") ax.legend() ax.set_xlim(-np.pi, np.pi) ax.set_ylim(-np.pi, np.pi) ax.set_zlim(-np.pi, np.pi) ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') plt.show()
3次元グラフの底面に画像を描画する
3次元でxyzを描画する際、グラフの底面に地図などの平面画像を描画したい場合がある。 その場合、画像をsurfaceとして読み込んで描画することで平面画像とグラフを同時に描画することが可能になる。
画像の読み込みは下記のように行う。
from PIL import Image from pylab import ogrid img = Image.open('mapImage.png') testImg = np.zeros([256,256,4]) testImg[:,:,0:3]= np.array(img.resize((256, 256)))/255 testImg[:,:,3]= 0.5 X1, Y1 = ogrid[0:testImg.shape[0], 0:testImg.shape[1]] X1 = X1 - 128 Y1 = Y1 - 128 ax.plot_surface(X1, Y1, np.atleast_2d(-50.0), rstride=5, cstride=5, facecolors=testImg)
上記例ではPILライブラリで画像を読み込んでから256×256に初期化する。 ここで、画像を0-1のfloatにするため全体を255で除算している。 また、画像の4次元目は透明度なので、0-1で任意の値に指定する。(0が透明、1が不透明) その後、pylabのogrid関数でsurface用のXY座標を取得する。ここで画像の中心を0にするために-128を引いて調整している。 最後にplot_surface関数で画像を描画している。np.atleast_2dの引数はz軸の値として任意に指定する。
サンプルコードと結果を下記に示す。
import matplotlib.pyplot as plt import numpy as np from mpl_toolkits.mplot3d import Axes3D from PIL import Image from pylab import ogrid fig = plt.figure() ax = fig.add_subplot(1,1,1,projection='3d') #subplotの追加 (行/列/描画対象インデックス,3次元軸の追加) #画像の読み込み img = Image.open('mapImage.png') testImg = np.zeros([256,256,4]) testImg[:,:,0:3]= np.array(img.resize((256, 256)))/255 testImg[:,:,3]= 0.5 # 画像を挿入(0など任意の値にどうぞ) X1, Y1 = ogrid[0:testImg.shape[0], 0:testImg.shape[1]] X1 = X1 - 128 Y1 = Y1 - 128 ax.plot_surface(X1, Y1, np.atleast_2d(-50.0), rstride=5, cstride=5, facecolors=testImg) # xy平面にsin関数を描画する xs = np.linspace(-np.pi, np.pi, 100) ys = np.sin(xs) ax.plot(xs*50, ys*50, zs=0, zdir='z', label='sin (x, y)') # xz平面にcos関数を描画する xs = np.linspace(-np.pi, np.pi, 100) ys= np.zeros([100]) zs = np.cos(xs) ax.scatter(xs*50, ys, zs*50, zdir='z', label='cos (x, y)',color="red") ax.legend() ax.set_xlim(-150, 150) ax.set_ylim(-150, 150) ax.set_zlim(-150, 150) ax.set_xlabel('X') ax.set_ylabel('Y') ax.set_zlabel('Z') plt.show()