2020-06-16

[Python] matplotlib の Surface (3D) プロット 座標について

3D の描画でハマったのでメモ。

プロット

まず線(Line plot) と 分布図(Scatter plot) から。

分布図と線は 2D の プロット と同じく1次元の配列を期待します。

簡単ですね。

今回メインで説明するのは plot_surface() 関数です。

info
  • plot_wireframe() は ほぼ同じです。
  • wireframe は 線だけで 物体を形取り surface はそれらの間に着色することで物体の形をよりリアルに表現できます。

さて、こいつが期待するのは 通常のプロットとは違い、2次元のネストしたデータです。

X, Y, Z Data values as 2D arrays

座標についてはこれだけの説明と申し訳程度の描画画像が 公式チュートリアル にあるわけですが、ちょっと何言ってるかわからなかったので自分で動作確認してようやくわかりました。

行列の 同じ行 もしくは 同じ列 の隣接した点を結ぶことで線の描画を行うというものらしいです。 行、列という情報を付加するために 2D にしたんですねぇ。

行/列
    • 0
    • 1
    • 2
    • 3
0
    • [0, 0]
    • [0, 1]
    • [0, 2]
    • [0, 3]
1
    • [1, 0]
    • [1, 1]
    • [1, 2]
    • [1, 3]
2
    • [2, 0]
    • [2, 1]
    • [2, 2]
    • [2, 3]
3
    • [3, 0]
    • [3, 1]
    • [3, 2]
    • [3, 3]

たとえば [1, 1] の点なら 上下左右の [0, 1], [2, 1], [1, 0], [1, 2] の点と結びます。

「行番号,列番号」と「X座標,Y座標」は別物なので混同しないでください。

この記事では行番号,列番号は 角括弧 [] で表し、座標は 丸括弧 () で表します。

以下操作ログ。座標は適当です。

先程の表に今度は 座標 を当てはめてみます。

行/列
    • 0
    • 1
    • 2
    • 3
0
    • (1,2,10)
    • (2,2,4)
    • (4,2,8)
    • (6,2,3)
1
    • (1,5,5)
    • (2,5,15)
    • (4,5,20)
    • (6,5,30)
2
    • (1,7,7)
    • (2,7,14)
    • (4,7,20)
    • (6,7,42)
3
    • (1,10,30)
    • (2,10,20)
    • (4,10,40)
    • (4,10,60)

この例では [1,1] 、 つまり xs[1,1], ys[1,1], zs[1,1] が作る空間座標 (2, 5, 15) は以下の隣り合う座標と線を結んでいるのがわかるでしょうか。

  • [0, 1]: (2, 2, 4)
  • [2, 1]: (2, 7, 14)
  • [1, 0]: (1, 5, 5)
  • [1, 2]: (4, 5, 20)

(画像を回転できないので見づらいと思いますが) その他の点も同じような規則で並んでいることを確かめてみてください

warning
  • plot_surface() ではデータが小数でないと以下のようなエラーが発生することがあるようです。
  • AttributeError: 'Float' object has no attribute 'dtype'
  • とりあえず numpy の Array であれば dtype='float' を指定するだけでOKです。

Mesh grid

3Dの描画に必要なデータ構造がわかりましたが、これを毎回手動で作成するのは手間です。

NumPy には 1次元のオブジェクトを組み合わせて多次元のメッシュ構造を作成する機能があります。

mgrid

mgrid は slice を指定することでメッシュ構造を作成するオブジェクトです。

ちょっと独特な書き方ですね。

上記の例でいうと

  • xs には が複製された Array
  • ys には が複製された Array

が補完されることにより、それぞれに対して同じ大きさのメッシュ構造が作成されるわけです。

今回は 2次元でしたが、3次元以上の構造も作成することができます。

meshgrid

こっちは 1次元のArray (list) を可変長引数として受けとる関数です。

meshgrid 関数は 行, 列 の順番が mgrid とは 逆っぽいです。 (mgrid が逆なのかな)

自分はこっちのほうが直感的で好みです。

その他参考

基本的な表面プロット - MATLAB surface - MathWorks 日本https://jp.mathworks.com/help/matlab/ref/surface.html numpy.mgrid — NumPy v1.21 Manualhttps://docs.scipy.org/doc/numpy/reference/generated/numpy.mgrid.html numpy.meshgrid — NumPy v1.14 Manualhttps://docs.scipy.org/doc/numpy-1.14.0/reference/generated/numpy.meshgrid.html 配列の要素から格子列を生成するnumpy.meshgrid関数の使い方NumPyの関数であるnp.meshgridはmatplotlibでグラフを描画する際、格子点を作りたいときや組み合わせを生成したいときに便利な機能です。本記事では、np.meshgridの使い方について解説しました。https://deepage.net/features/numpy-meshgrid.html