Pythonのグラフ描画ライブラリであるmatplotlibは論文で使われるレベルで世間に認知されています。さらに、通常の2Dグラフプロットコードに少し手を加えるだけで3Dプロットも簡単なコードで実現可能です。 いくつかの例題を用いて、matplotlibによる3D散布図の作成方法を紹介します。
こんにちは。wat(@watlablog)です。
大変便利なmatplotlibですが、今回は3D散布図を作るためのデータの用意やプロットに必要なコードを紹介します!
3Dプロットを良く使うデータ2選
3Dプロットとは、x軸、y軸だけではなく、z軸も使ってデータを表現するグラフプロットの種類です。
実験結果やシミュレーション結果は、極力3次元的な表現を避けて説明した方が好まれると思いますが、中には3Dプロットをした方が効果的なプレゼンが出来る場合もあります。
ここではどんなデータが3Dプロットに適しているかを紹介します。
サーフェスデータ
1つ目はサーフェス(面)データを表現しなければならない場合についてです。
サーフェスでデータを表現した例を下図に示します。
この図は\(z=sin(x)cos(x)\)を意味していますが、シミュレーションをやっている方の中にはこのような面の勾配情報を計算して解を探査している人も多いのではないでしょうか?
時間だけでなく空間も変数にとっている問題等に多いと思います。
また、流体解析の波の時間変化を可視化する際にも、このようなサーフェスを表現することで他者へ効果的に説明ができるものと考えられます。
3次元離散データ
2つ目は3次元的なひろがりを持った離散データです。
下図に例を示します。
これは実験やシミュレーションにおいて、2つのパラメータを任意に振った時のある値の応答をとった時によく使われるデータです。
上記画像は平面の方程式を元にノイズを含ませて作ったプロットです。
実験データ等も様々なノイズを含んでいるので、似たようにばらばらしたデータとなることが多いです。
そのような場合にまず3D散布図にプロットしてみるということを行います。
一度散布図にプロットしてデータの傾向を観察した後に、下図のように適切な回帰曲面を作成して応答曲面としてデータを利用するといったことも良くやられていると思います。
例えばFEM(有限要素法)を使って様々な条件を振って解析を行い、得られたデータから応答曲面を作成して別の解析で高速計算に使用するということもよくやられますね。
Python/matplotlibで3Dプロットするコード
サーフェスデータをプロットする
サンプルコード(ワイヤーフレーム表示)
まず始めに先ほど紹介した図のようにサーフェスデータをワイヤーフレーム表示でプロットする方法を紹介します。
以下のコードをそのままコピペして実行することで結果を得ることが出来ます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
import numpy as np from matplotlib import pyplot as plt # データを用意する x = np.arange(0, 10, 0.2) # x軸を作成 y = np.arange(0, 10, 0.2) # y軸を作成 X, Y = np.meshgrid(x, y) # x軸とy軸からグリッドデータを作成 Z = np.sin(X) * np.cos(Y) # グリッドデータに基づき曲面データを作成 # ここからグラフ描画 # フォントの種類とサイズを設定する。 plt.rcParams['font.size'] = 14 plt.rcParams['font.family'] = 'Times New Roman' # グラフの入れ物を用意する。 fig = plt.figure() ax1 = fig.add_subplot(111, projection='3d') # 軸のラベルを設定する。 ax1.set_xlabel('x') ax1.set_ylabel('y') ax1.set_zlabel('z') # データプロットする。 ax1.plot_wireframe(X, Y, Z, label='Dataset') plt.legend() # グラフを表示する。 plt.show() plt.close() |
3Dのデータの表現は色々ありますが、このサーフェスデータの表現にはグリッドを作ると綺麗な面を描画することができます。
「#データを用意する」というコメントに記載された部分で、まずx軸とy軸を準備し、その後np.meshgridでメッシュグリッドを作っています。
このメッシュグリッドに基づいてz軸の値を計算することで、メッシュのラインが面を表現しやすくなります。
ちなみにこのようなサーフェスを表現するためのデータを2変数スカラーデータとも言います。
3Dプロットのための特別な文としては、「#グラフの入れ物を用意する。」のコメントがある部分と、「#データをプロットする。」でplot_wireframe(X, Y, Z)としている部分があります。
上記コードを実行することで以下の図を得ることが出来ます。
その他は「Pythonのmatplotlibをオブジェクト指向で使う!」で紹介した時とほぼ同じ使い方ですので、matplotlibの基本的なコードを確認したい方は是非そちらの記事も参照してみて下さい。
サンプルコード(カラーマップ表示)
上記コードのプロット部分を以下の文に変更することで、カラーマップ表示にすることも可能です。
1 2 |
# データプロットする。 ax1.plot_surface(X, Y, Z, cmap='jet') |
カラーマップの色はcmapというパラメータで変更が可能です。
当WATLABブログでは「Pythonで音のSTFT計算を自作!スペクトログラム表示する方法」でもこのcmapを使っていますが、全く同じ設定です。
以下の図はcmap違いの3Dプロットです。「matplotlib公式サイト:color example code」に良いリストがありますので、お好みの設定を見つけて下さい。
3次元離散データをプロットする
サンプルコード
次に3次元離散データのプロット方法を紹介します。
ここでは例題として「#データを用意する」の部分で以下の式(平面の方程式)からデータを作成しプロットしています。
\[z=w_{0}+w_{1}x+w_{2}y\]
この式をそのまま描画すると綺麗な平面が出来上がるだけでつまらないので、np.randomを使ってデータにノイズ成分を含ませています。
実際には2変数xyの変化とそれに応じた応答zの実験データがリストであれば、それを読み込み、以下コードのxyzに代入するだけで良いでしょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
import numpy as np from matplotlib import pyplot as plt # データを用意する w0 = 1.0 # 定数 w1 = 1.0 # 係数1 w2 = 2.0 # 係数2 x = np.random.uniform(0, 10, 300) # ノイズを含んだx軸を作成 y = np.random.uniform(0, 10, 300) # ノイズを含んだy軸を作成 # ノイズを含んだ平面点列データを作成 z = w0 + (w1 * x) + (w2 * y) + np.random.uniform(0, 5, 300) # ここからグラフ描画 # フォントの種類とサイズを設定する。 plt.rcParams['font.size'] = 14 plt.rcParams['font.family'] = 'Times New Roman' # グラフの入れ物を用意する。 fig = plt.figure() ax1 = fig.add_subplot(111, projection='3d') # 軸のラベルを設定する。 ax1.set_xlabel('x') ax1.set_ylabel('y') ax1.set_zlabel('z') # データプロットする。 ax1.scatter3D(x, y, z, label='Dataset') plt.legend() # グラフを表示する。 plt.show() plt.close() |
今回は散布図を作るので、scatter3Dを使うという所が先ほどと異なる点です。
まとめ
本記事では3Dプロットで表現することが多い2種類のデータについて解説し、
実際にmatplotlibを使ったプロットコードを紹介しました。
通常の2Dプロットに、import文でmpl_toolkits.mplot3dを追加、Axes3Dでグラフの入れ物を作ってplot_wireframeやplot_surface、scatter3Dを使うことで簡単に3Dプロットができることを確認しました。
3Dプロットの扱い方がわかってきました!3Dプロットは今後データサイエンスの分野でも多用しそうなのでよく理解しておこう!
Twitterでも関連情報をつぶやいているので、wat(@watlablog)のフォローお待ちしています!
3Dグラフですが、マウスでドラッグして視点をいろいろ変えられないのでしょうか?
ご訪問ありがとうございます。
Pythonで動作させている時はマウスで視点を自由に変更することができますよ。
(このページのグラフは画像を貼り付けているだけです)