PlotlyのPython用ライブラリで3Dプロットを試してみた

2022.02.16

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

データアナリティクス事業本部の鈴木です。

今回は、PlotlyのPython用ライブラリを使って3次元表面を描画する方法をまとめてみました。

Plotlyの公式ドキュメントで3Dプロットを紹介している、『3D Surface Plots』を参考にしています。

3D Surface Plots

ドキュメントでは描画の設定の記載もありますが、この記事ではどのようなインプットをすると3Dプロットができるのかというところを中心にご紹介します。

また、ドキュメントに載っていない関数も描いてみようと思い、1例だけですが別の関数も描いてみました。

検証環境

Jupyter Docker Stacksのdatascience-notebookイメージにPlotlyをインストールして検証しました。バージョンなどは以下になります。

  • イメージ:jupyter/datascience-notebook
  • Plotly 5.6.0

使い方を確認してみる

ドキュメントに紹介されている2つの例を試してみました。

どちらもplotly.graph_objects.Surfaceを作成し、plotly.graph_objects.Figureに渡して表示する流れになります。

まず、Surfaceを配列から作成して、どのようにインスタンスを作るか確認します。

以下のコードを作成し、実行することで、どのような3Dプロットができるか確認します。

# https://plotly.com/python/3d-surface-plots/
# を2022/02/15に改変しました。

import plotly.graph_objects as go

sur = go.Surface(
    contours = {
        "x": {"show": True, "start": 1.5, "end": 2, "size": 0.04, "color":"white"},
        "z": {"show": True, "start": 0.5, "end": 0.8, "size": 0.05}
    },
    x = [1,2,3,4,5],
    y = [1,2,3,4,5],
    z = [
        [0, 1, 0, 1, 0],
        [1, 0, 1, 0, 1],
        [0, 1, 0, 1, 0],
        [1, 0, 1, 0, 1],
        [0, 1, 0, 1, 0]
    ])

fig = go.Figure(sur)
fig.update_layout(
        scene = {
            "xaxis": {"nticks": 20},
            "zaxis": {"nticks": 4},
            'camera_eye': {"x": 0, "y": -1, "z": 0.5},
            "aspectratio": {"x": 1, "y": 1, "z": 0.2}
        })
fig.show()

実行すると以下のような3Dプロットが描画されます。3次元でぐるぐる回転させることが可能です。

Surfacexyに渡した配列と、zに渡した2次元配列の対応する値が3Dプロットの座標に各々対応しています。

表示例1

次にpandasのDataFrameから作成する場合も確認しておきます。この場合は、valuesで2次元配列にしてから、先ほどと同じようにSurfaceに渡す形になります。

# https://plotly.com/python/3d-surface-plots/
# を2022/02/15に改変しました。

import plotly.graph_objects as go

import pandas as pd

# Read data from a csv
z_data = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/api_docs/mt_bruno_elevation.csv', index_col=0)

fig = go.Figure(data=[go.Surface(z=z_data.values)])

fig.update_layout(title='Mt Bruno Elevation', autosize=False,
                  width=500, height=500,
                  margin=dict(l=65, r=50, b=65, t=90))

fig.show()

実行すると以下のような3Dプロットが描画されます。

表示例2

xおよびyを指定しない場合は、x軸とy軸が0からになります。

オリジナルの例を試してみる

使い方が分かったので、自分でも例を作ってみました。

今回は鞍点がある関数を表示してみました。

以下のように、描画したい関数fを定義し、適当なxyの範囲でzの値を計算してSurfaceに渡します。

# 表示したい関数
def f(x, y):
    return x*x - y*y

# 表示するデータの作成
xs = [x for x in range(-10, 10)]
ys = [y for y in range(-10, 10)]
zs = [[f(x, y) for x in xs] for y in ys]   

# 表示
sur = go.Surface(
    contours = {
        "x": {"show": True, "start": 1.5, "end": 2, "size": 0.04, "color":"white"},
        "z": {"show": True, "start": 0.5, "end": 0.8, "size": 0.05}
    },
    x = xs,
    y = ys,
    z = zs
)

fig = go.Figure(sur)
fig.update_layout(
        scene = {
            "xaxis": {"nticks": 20},
            "zaxis": {"nticks": 4},
            'camera_eye': {"x": 1, "y": -1.5, "z": 1},
            "aspectratio": {"x": 1, "y": 1, "z": 1}
        },
        width=800,
        height=800)
fig.show()

実行してみます。

表示例3

意図した通りに描画することができました。

意図している関数を描く場合は、xyに値を渡し、zと対応がついている状態になっているか注意が必要です。また、update_layoutaspectratioが設定されている場合には、直感と違う形で描写される可能性があるので、1:1:1にしておくのが無難そうです。

おわりに

今回はPlotlyの公式ドキュメントのうち3Dプロットを紹介している、3D Surface Plotsに記載の例と、それを元に自分で別の例を描画してみました。

Plotlyの3Dプロットを使うと、とても簡単にインタラクティブな3次元表面が描けるので、この関数どんな表面になってるんだっけ?というときにもPythonで気軽に確認できて良いですね。

参考