Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Plotly

Plotly はインタラクティブなグラフを描くライブラリである.マウスでの回転・拡大や,アニメーションの再生ができる. 作成した図は Colab・JupyterLab でも,ビルド後の Web ページ上でも同じように動作するため,本演習ではインタラクティブな可視化・アニメーションの標準として用いる.

graph_objectsgo という別名で読み込む.

import numpy as np
import plotly.graph_objects as go

基本:折れ線・散布図

go.Figure に描画要素(トレース)を渡して図を作り,fig.show() で表示する. 折れ線・散布図には go.Scatter を使い,mode で線・点を切り替える.

x = np.linspace(0, 2 * np.pi, 100)

fig = go.Figure()
fig.add_trace(go.Scatter(x=x, y=np.sin(x), mode="lines", name="sin"))
fig.add_trace(go.Scatter(x=x, y=np.cos(x), mode="lines", name="cos"))
fig.update_layout(title="sin and cos", xaxis_title="x", yaxis_title="y")
fig.show()
Loading...

2次元の場:ヒートマップ

格子上のスカラー場は go.Heatmap で色として可視化できる.セルオートマトンや拡散場の表示に使う.

grid = np.linspace(-3, 3, 60)
X, Y = np.meshgrid(grid, grid)
Z = np.exp(-(X**2 + Y**2))

fig = go.Figure(go.Heatmap(z=Z))
fig.update_layout(title="2D Gaussian", width=500, height=500)
fig.show()
Loading...

3次元:曲面と散布

go.Surface は3次元の曲面を描く.update_layoutscene で見た目を整える. aspectmode="data" はデータの比率を保ち,形態の可視化(P2-01)で重要になる.

fig = go.Figure(go.Surface(x=X, y=Y, z=Z, showscale=False))
fig.update_layout(scene={"aspectmode": "data"}, title="3D surface")
fig.show()
Loading...

3次元の点群は go.Scatter3d で描ける.

theta = np.linspace(0, 6 * np.pi, 200)
fig = go.Figure(go.Scatter3d(x=np.cos(theta), y=np.sin(theta), z=theta, mode="lines"))
fig.update_layout(scene={"aspectmode": "data"}, title="helix")
fig.show()
Loading...

アニメーション

時間発展するシミュレーション(セルオートマトンや拡散)は,各時刻の状態をフレーム (frame)として並べてアニメーションにする. go.Frame のリストを frames に渡し,再生ボタンとスライダーを付ける.このパターンを P2-02・P2-03 でそのまま使う.

まず,各時刻の場(2次元配列)のリストを用意する(ここでは広がるガウス分布).

fields = []
for t in np.linspace(0.3, 2.5, 30):
    fields.append(np.exp(-(X**2 + Y**2) / (2 * t**2)))

各フレームを go.Frame にして図を組み立てる. 再生速度は「1フレームあたりの表示時間(ミリ秒)」で決まる.速度の異なる再生ボタンを複数用意しておくと切り替えられて便利である.

frames = [
    go.Frame(data=[go.Heatmap(z=f, zmin=0, zmax=1)], name=str(k))
    for k, f in enumerate(fields)
]

fig = go.Figure(data=[go.Heatmap(z=fields[0], zmin=0, zmax=1)], frames=frames)


def play_button(label, duration):
    """1フレームを duration ミリ秒で表示して再生するボタン(小さいほど速い)"""
    return {
        "label": label,
        "method": "animate",
        "args": [
            None,
            {
                "frame": {"duration": duration, "redraw": True},
                "fromcurrent": True,
                "transition": {"duration": 0},
            },
        ],
    }


fig.update_layout(
    width=600,
    height=640,
    margin={"l": 20, "r": 20, "t": 70, "b": 20},
    updatemenus=[
        {
            "type": "buttons",
            "direction": "right",
            "showactive": False,
            "x": 0.5,
            "xanchor": "center",
            "y": 1.1,
            "yanchor": "bottom",
            "buttons": [
                play_button("Play", 120),
                play_button("Fast", 40),
                play_button("Slow", 300),
                {
                    "label": "Pause",
                    "method": "animate",
                    "args": [[None], {"frame": {"duration": 0}, "mode": "immediate"}],
                },
            ],
        }
    ],
    # スライダー(フレームを手動で選ぶ)
    sliders=[
        {
            "steps": [
                {"label": str(k), "method": "animate", "args": [[str(k)]]}
                for k in range(len(frames))
            ]
        }
    ],
)
fig.show()
Loading...

PlayFastSlow で再生速度を切り替えられ,スライダーで任意の時刻に移動できる(redraw=True はヒートマップの更新に必要). 2次元配列のリスト fields を差し替えるだけで,さまざまなシミュレーションの可視化に応用できる.