カテゴリー

go

【plotly&subplot】goでアニメーションのサブプロット

2021年11月13日

こんな人にオススメ

plotlygoでサブプロットしながらアニメーションを描きたいんだけど、どうしたらいけそう?

goの場合だとアニメーションがごちゃごちゃするから書き方がわからん。

ということで、今回はplotlyplotly.graph_objectsgoを使ってサブプロットを描きつつ、各グラフをアニメーションにする方法を解説する。

以下の記事でsubplotsanimation単体については解説している。今回はこれらの合体版。

【Plotly&animation】Plotlyのgoでアニメーションを作成

続きを見る

【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く

続きを見る

本記事で作成するグラフは9種類の関数の3 x 3プロットだが、もちろんデータを適用してもいい。自分で5 x 5とかにもできるから適したグラフを作成してほしい。

python環境は以下。

  • Python 3.9.7
  • numpy 1.21.3
  • plotly 5.3.1
  • plotly-orca 3.4.2

運営者のメガネです。YouTubeTwitterInstagramも運営中。自己紹介お問い合わせページあります。

運営者メガネ

作成したコード全文

下準備

import numpy as np
from plotly.subplots import make_subplots
import plotly.graph_objects as go
import plotly.io as pio

まずは下準備としてのimport関連。goのsubplotには2種類の作成方法があるが、今回は自動で位置調節をしてくれるmake_subplotsを使用。

domainってのもあるけど、こっちは各グラフのサイズを指定したりしないといけないので自由度は高いが面倒。

使用データ

# -πから+πまでの-0.03141(π * 0.01)刻みのデータ
x = np.arange(-np.pi, np.pi * 1.04, np.pi * 0.04)
print(x)
# [-3.14159265e+00 -3.01592895e+00 -2.89026524e+00 -2.76460154e+00
#  -2.63893783e+00 -2.51327412e+00 -2.38761042e+00 -2.26194671e+00
#  -2.13628300e+00 -2.01061930e+00 -1.88495559e+00 -1.75929189e+00
#  -1.63362818e+00 -1.50796447e+00 -1.38230077e+00 -1.25663706e+00
#  -1.13097336e+00 -1.00530965e+00 -8.79645943e-01 -7.53982237e-01
#  -6.28318531e-01 -5.02654825e-01 -3.76991118e-01 -2.51327412e-01
#  -1.25663706e-01  4.44089210e-15  1.25663706e-01  2.51327412e-01
#   3.76991118e-01  5.02654825e-01  6.28318531e-01  7.53982237e-01
#   8.79645943e-01  1.00530965e+00  1.13097336e+00  1.25663706e+00
#   1.38230077e+00  1.50796447e+00  1.63362818e+00  1.75929189e+00
#   1.88495559e+00  2.01061930e+00  2.13628300e+00  2.26194671e+00
#   2.38761042e+00  2.51327412e+00  2.63893783e+00  2.76460154e+00
#   2.89026524e+00  3.01592895e+00  3.14159265e+00]

まずは今回使用するデータ。今回は三角関数を使うのでπを基準に横軸のデータを作成した。範囲は-πから+πで0.04π刻み。

大雑把な刻みにするとグラフになめらかさがなくなるが、細かくするとデータが重くなるのでいい塩梅のところを選択。

この横軸の値から以下のように9種類の縦軸の値を作成。順番はグラフ化した時の関係でちょっとイレギュラーになっている。

  1. 直線(1次関数)
  2. 無理関数(ルート)
  3. 対数関数
  4. 2次関数
  5. sin関数
  6. cos関数
  7. 3次関数
  8. 指数関数
  9. sin関数と指数関数の組み合わせで減衰系のプロットを再現
# yとして使用する関数
ys = {
    'linear': x,  # 1次関数
    'unreasonable': np.sqrt(x),  # 無理関数
    'log': np.log(x),  # log関数
    'quad': x ** 2,  # 2次関数
    'sin': np.sin(x),  # sin関数
    'cos': np.cos(x),  # cos関数
    'cubic': x ** 3,  # 3次関数
    'exponential': np.exp(x),  # 指数関数
    # めんどい関数
    '5sin(8x)*e<sup>-0.5x</sup>': 5 * np.sin(8 * x) * np.exp(-0.5 * x),
}

また、アニメーションの場合はグラフの表示範囲を設定する必要がある。横軸はxの最小値・最大値から調節するが縦軸はysの値から以下のように設定した。

# 各関数の表示範囲
ranges = {
    'linear': (-4, 4),
    'unreasonable': (-4, 4),
    'log': (-4, 4),
    'quad': (-2, 10),
    'sin': (-2, 10),
    'cos': (-2, 10),
    'cubic': (-35, 35),
    'exponential': (-35, 35),
    '5sin(8x)*e<sup>-0.5x</sup>': (-35, 35),
}

ysの関数の順番が変なのはこの表示範囲に関係している。今回は3 x 3のグラフを作成するが、ysの上から3つずつで同じくらいの縦軸の値にしている。

こうすることで、軸を共有した時に見た目がキレイになる。一方で全てのグラフでスケールが一緒というわけではないので比較するという目的では適さない。

ysを1つのグラフにしたのが以下。save関数はこの後で解説する自作の関数。グラフの保存を担う。


data = [
    go.Scatter(
        x=x, y=y, name=name,
        mode='lines+markers') for name, y in ys.items()
]
fig = go.Figure(data=data)
# fig.show()
save(fig=fig, config=None, save_name='all_plot')

グラフを作成するための関数たち

ここでは予め定義しておく関数を紹介しておく。先程のsave関数のようなもの。予め関数を定義しておくとメインの部分でコードがごちゃごちゃしなくなってスッキリする。

また、複数箇所で同じコードを使用するときも関数があればその関数を使えばいい。修正するときも関数だけ修正したら全てで反映される。

【plotly&工夫】楽にグラフを描くためのplotlyの関数化

続きを見る

サブプロット作成用の関数

# subplotの雛形
def set_subplots(rows, cols):
    # sibplot用のfig作成
    fig = make_subplots(
        rows=rows, cols=cols,  # 行数と列数
        shared_xaxes=True,  # 横軸を共有
        shared_yaxes=True,  # 縦軸を共有
        subplot_titles=list(ys),  # 各グラフのタイトル
    )
    return fig

まずはサブプロット作成用の関数。make_subplotsを使用する際には予めサブプロットをすることを明記しておかないといけない。

rowscolsで行数と列数を指定する。今回は3 x 3にする。shared_xaxes, shared_yaxesは横軸・縦軸を他のグラフを共有するか否か。共有すると軸ラベルと目盛が左端と下端だけになりスッキリする。

subplot_titleは各サブプロットのグラフのタイトル。ここで設定しないといけない。今回はyskeysを使用し、各関数の名称をタイトルとした。

ボタン作成用の関数

# ボタン内容の設定
def set_buttons():
    # ボタン内容の設定
    transition = {'duration': 100, 'easing': 'linear'}
    animation = {
        'frame': {'duration': 20, 'redraw': False},
        'transition': transition,
    }
    # ボタンの細かい設定
    args = [None, animation]
    buttons = [dict(label='Play', method='animate', args=args,)]
    return buttons

アニメーションの開始ボタンを作成するためにはボタンの設定が必要。ということで、ボタン用の関数を定義。

durationはコマごとの遅延。easingはコマが進むときの進み方。スッと進むのかバウンスしながら進むのかなど設定可能。

【plotly&animation】動き方のeasingと遅延のduration

続きを見る

redrawはコマごとにプロットの再描画をするかどうか。Falseにするとプロットの追加のみを行う。

グラフ保存用の関数

# グラフ保存用の関数
def save(fig, config, save_name):
    pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
    pio.write_html(fig, f"{save_name}.html", config=config,)
    pio.write_image(fig, f"{save_name}.png")

まずはグラフ保存用の関数。保存形式はhtmlとpng。configはグラフの右上に存在するツールバーのようなもの。

configを設定しておくとこのツールバーの内容を追加したり削除したりできる。htmlで使用することで、保存後のhtmlファイルでもツールバーの設定が引き継がれる。

【plotly&config】グラフのツールバーを編集する

続きを見る

今回はサクッと作りたいだけなのでconfigは使用しない。なのでconfig=Noneとして引数を処理することにする。

アニメーションのsubplotグラフを作成


ということで、アニメーションのsubplotグラフを作成。大まかな流れは以下。

  1. サブプロットのベース作成
  2. 各サブプロットの行と列を指定
  3. 初期表示のプロット作成
  4. サブプロットのどのグラフに描画するか設定
  5. 初期値作成のついでに各グラフの表示範囲も設定
  6. ボタンを作成+ボタンをレイアウトに配置
  7. xごとに全ysのプロットでそのx以前までのデータをプロット(軌跡の描画)
  8. データをフレームに格納
  9. フレームをグラフに適用
  10. グラフの表示と保存

項目としては多いけど、内容的には1行で済むものも多いので意外とスッキリする。

サブプロットの各点はxごとにそれまでのxで描かれた各ysのプロットを描くことで軌跡のようにプロットすることができる。

なお、アニメーションのプロットは左上から降ってくるようなので、途中までプロットされない無理関数(unreasonable)と対数関数(log)では左上に謎の点が出現する。

この謎の点の動きが知りたい場合は、mode='text'にして好きなテキスト(以下のコードでは各関数名)を指定すると動きがよくわかるようになる。

def animation_subplot(save_name):
    fig = set_subplots(rows=3, cols=3,)

    # 初期データの作成
    num = 0
    for num, (name, y) in enumerate(ys.items()):
        row = num // 3 + 1  # 行の位置
        col = num % 3 + 1  # 列の位置

        # 初期表示のプロットは各関数の最初の値
        d = go.Scatter(x=(x[0],), y=(y[0],), name=name)
        fig.append_trace(d, row, col)  # グラフをグリッドに配置

        # 各グリッドの表示範囲を決める
        dct = dict(row=row, col=col)
        fig.update_xaxes(range=(-3.5, 3.5), **dct)
        fig.update_yaxes(range=ranges[name], **dct)

        # ボタン全体の設定
        updatemenus = dict(type='buttons', buttons=set_buttons(),)
        # レイアウトにボタンを配置する
        fig.update_layout(showlegend=False, updatemenus=[updatemenus])

    # アニメーションの設定
    data = []  # 各xごとにそれ以前の各関数の値を含めたプロットを格納
    frames = []
    for num, _ in enumerate(x):
        plot = []  # 各xごとの各関数のプロット点を格納
        for name, y in ys.items():
            d = go.Scatter(
                x=x[:num + 1], y=y[:num + 1],  # 1点ずつプロットが増えるようにする
                name=name, mode='lines+markers',
                # mode='text', text=name  # 関数名をテキストにしたい場合
            )
            plot.append(d)

        # 過去の分も含めてフレームに入れる
        data.append(go.Frame(data=plot, name=num))
        frames.append(data)

    # フレームをfigに適用
    fig['frames'] = frames[0]  # ムダにlistがあるから外す

    # グラフの表示と保存
    fig.show()
    save(fig=fig, config=None, save_name=save_name)

animation_subplot(save_name='animation_subplot')

複数グラフの動きを見るには良い

今回はplotlygoを使用して、アニメーション機能を持ったサブプロットを作成した。これで変化を気にしなければいけないような複数グラフを一気に閲覧することができる。

また、シンプルに移動した先の点だけをプロットするのではなく、それまでのプロットも軌跡としてプロットすることで、さらに変化がわかりやすいだろう。

今回のグラフを強化したいなら、一時停止ボタンや各フレームのスライダーの追加だろう。ここを設定するとなるとかなり面倒だができると機能性が爆上げするので是非とも挑戦していただきたい。

関連記事

【plotly&go.Scatter】plotlyの散布図グラフの描き方

続きを見る

【plotly&スライダー】plotlyのslidersにスライダーを追加

続きを見る

【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加

続きを見る

【plotly&レーダーチャート】plotlyのRadar Chartの使い方とか設定とか

続きを見る

ガジェット

2023/9/18

【デスクツアー2022下半期】モノは少なく、でも効率的に Desk Updating #0

今回はガジェットブロガーなのにデスク環境を構築していない執筆者の ...

ライフハック

2023/9/16

【Audible vs YouTube Premium】耳で聴く音声学習コンテンツを比較

ワイヤレスイヤホンが普及し耳で学習することへのハードルが格段に下 ...

完全ワイヤレスイヤホン(TWS)

2023/9/18

【SENNHEISER MOMENTUM True Wireless 3レビュー】全てが整ったイヤホン

今回は高音質・高機能なSENNHEISERのフラグシップ完全ワイヤレスイヤホン「SENNH ...

ライフハック

2023/3/11

【YouTube Premiumとは】メリットしかないから全員入れ

今回はYouTube Premiumを実際に使ってみてどうなのか、どんなメリット/デメリット ...

マウス

2023/8/17

【Logicool MX ERGOレビュー】疲れない作業効率重視トラックボールマウス

こんな人におすすめ トラックボールマウスの王道Logicool MX ERGOが気になるけどऩ ...

ベストバイ

2023/9/18

【ベストバイ2022】今年買って良かったモノのトップ10

2022年ベストバイ この1年を振り返って執筆者は何を買ったのか。ガジェッ& ...

スマホ

2023/1/15

【楽天モバイル×povo2.0の併用】月1,000円の保険付きデュアルSIM運用

こんな人におすすめ 楽天モバイルとpovo2.0のデュアルSIM運用って実際のとこ ...

マウス

2023/9/16

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

macOSアプリケーション

2022/9/30

【Chrome拡張機能】便利で効率的に作業できるおすすめの拡張機能を18個紹介する

こんな人におすすめ Chromeの拡張機能を入れたいけど、調べても同じような ...

macOSアプリケーション

2023/5/3

【Automator活用術】Macで生産性を上げる作業の自動化術

今回はMacに標準でインストールされているアプリ「Automator」を使ってできる ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

Webエンジニア駆け出し。独学のPythonで天文学系の大学院を修了。常時金欠のガジェット好きでM2 Pro MacBook Pro(30万円) x Galaxy S22 Ultra(17万円)使いの狂人。自己紹介と半生→変わって楽しいの繰り返しレビュー依頼など→お問い合わせ運営者情報、TwitterX@m_ten_pa、 YouTube@megatenpa、 Threads@megatenpa

-go
-, , ,