当サイトはアフィリエイトプログラムによる収益を得ています〈景品表示法に基づく表記です)

Plotly全般の知識

【Plotly&sliders】スライダーを追加しデータを切り変える

2023年1月8日

本記事ではPlotlyでデータの流れを簡単に理解できる機能のスライダーについてその仕組みやグラフの例を解説・紹介する。

スライダー機能を使えるようになると時系列のグラフやsin(2x), sin(3x)といった一部だけ変わるグラフをわかりやすく表現することができる。

Plotlyボタン機能updatemenus)でも実現はできるが、この手のグラフはスライダー機能が断然わかりやすい。本記事では簡単なグラフから複数種のデータまで扱っているので是非ともスライダーをマスターしてほしい。

Python環境は以下。

  • Python 3.10.8
  • numpy 1.24.0
  • plotly 5.11.0
  • plotly-orca 3.4.2

参考になるサイト

Plotly公式

楽しみながら理解するAI・機械学習入門

Qiita

本記事のコード全文

下準備のimport

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

まずは下準備としてのimport関連。今回は基本goを使用する。pioplotlyでのグラフ保存用のライブラリ。保存の仕方は色々あるがpioはその1つだ。

スライダーなしのグラフ


まずはスライダーなしのプロットから。使うのはgo.Scatterでできるプロット(グラフ)は散布図だ。go.Scatterの詳しい引数やグラフの作成方法については下記の記事参照。

【Plotlyで散布図】go.Scatterのグラフの描き方まとめ

これからPloltyで動くグラフを作りたい、もっとグラフをキ ...

続きを見る

プロットは3種類のy=axの形式の一次関数。これらをそれぞれy0, y1, y2として定義し変数plotgo.Scatterして格納する。これでプロットが完成。

あとはgo.Layoutでグラフレイアウトを設定し、go.Figureでグラフを作成して表示・保存で完成だ。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# 3つのグラフをシンプルにグラフ化

x = np.arange(5)
y0 = 0 * x
y1 = 1 * x
y2 = 2 * x

# 配列の要素にプロット内容を格納
plot = [
    go.Scatter(x=x, y=y0),
    go.Scatter(x=x, y=y1),
    go.Scatter(x=x, y=y2),
]
# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20  # ホバーのフォントサイズ
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_3plots"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

slidersでグラフにスライダーを追加


続いては本記事のメイン内容のスライダーの追加だ。スライダーの考え方は以下の通り。

あらかじめ全ステップ分のプロットを作成。

ステップごとにこれらのグラフの表示・非表示を切り替える。

あらかじめ全プロットを作成してどのタイミングで見せるのかという問題なだけ。スライダーの各ステップに移動するたびにグラフを作成するわけではない。

スライダーの作成方法は少しややこしいので以下で順番に解説する。上のグラフを作成するための全コードはこの章の最後に記載した。

  • プロットするデータの用意

    import numpy as np
    import plotly.graph_objects as go
    import plotly.io as pio
    
    x = np.arange(5)
    y0 = 0 * x
    y1 = 1 * x
    y2 = 2 * x
    
    # 配列の要素にプロット内容を格納
    plot = [
        go.Scatter(x=x, y=y0),
        go.Scatter(x=x, y=y1),
        go.Scatter(x=x, y=y2),
    ]
    

    まずは先ほどのスライダーなしのグラフと同じように一次関数のデータを作成。ここで作成した3データを1プロットずつスライダーで表示・非表示にする。

  • スライダーの各ステップの処理を作成

    # y0表示用のステップ作成
    step0 = dict(
        label=0,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=[True, False, False]),  # y0のみ表示
            dict(title='y = 0x'),  # グラフタイトル
        ]
    )
    # y1表示用のステップ作成
    step1 = dict(
        label=1,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=[False, True, False]),  # y1のみ表示
            dict(title='y = 1x'),  # グラフタイトル
        ]
    )
    # y2表示用のステップ作成
    step2 = dict(
        label=2,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=[False, False, True]),  # y2のみ表示
            dict(title='y = 2x'),  # グラフタイトル
        ]
    )
    

    続いてはスライダーの各ステップの処理を作成。各ステップごとに辞書型でステップの処理を追記するが、それぞれの引数の意味は以下。詳しくは公式ドキュメント参照。

    • label:スライダーの各ステップのラベル
    • method:スライダーの影響範囲
      • restyle:データまたはデータ属性の変更
      • relayout:レイアウト属性の変更
      • updaterestylerelayoutの両方の変更が可能
      • animate:アニメーションの作成
    • args:各ステップの動作内容(下記はmethod=updateの時)
    • 1つ目のdict:データ関連の処理
    • 2つ目のdict:レイアウト関連の処理

    これらの他にも多数の引数があるが、ここでは基礎的な内容として最小限に留めておく。詳しくは公式ドキュメント参照。

    今回のスライダーではmethod=updateに設定し、argsの1つ目のdictvisibleを設定した。visibleはスライダーの各ステップに移動した際に以下の動作をするという意味だ。

    • visible=[True, False, False]:1つ目のプロットy0だけ表示
    • visible=[False, True, False]:2つ目のプロットy1だけ表示
    • visible=[False, False, True]:3つ目のプロットy2だけ表示

    argsの2つ目のdictではレイアウト関連の設定ができるが、ここではわかりやすいようにグラフタイトルを設定しておいた。

  • # スライダーをひとまとめにする
    steps = [step0, step1, step2]
    

    作成したスライダー処理は1つの配列に格納する必要がある。ここではlist形式にしたがもちろんtupleでも問題ない。

    plotlyでは複数の処理を作成したらこのように配列に入れることが多いのでこの機会に慣れておこう。

  • # レイアウトに設置するためのスライダー作成
    sliders = [
        dict(
            active=0,  # 初期状態で表示するプロットのインデックス
            # 各スライダーの傾きをスライダーの上に表示
            currentvalue=dict(prefix='傾き = '),
            steps=steps  # 作成したスライダー
        )
    ]
    

    先ほどは各ステップの処理を設定したが、ここではスライダー自体のレイアウトを設定する。今回設定した内容は以下。もちろんこれら以外にも設定内容があるので、詳しくは公式ドキュメントを参照いただきたい。

    • active:グラフ表示時に位置するステップ
    • currentvalue:グラフ下部の各ステップのラベル
    • steps:ステップの処理の内容
    • type:ボタンの種類

    引数activeはわかりづらいが、グラフが表示された際、1つ前のステップで作成した変数stepsの何番目のステップを選択するかの設定だ。

    acrive=0の場合だと変数stepsの0番目、すなわちstep0(プロットで言えばy0)が押された状態となる。なお、押されていない状態にしたかったら-1を指定する。

    Parent: layout.sliders[]

    Type: number greater than or equal to 0

    Default: 0

    Determines which button (by index starting from 0) is considered active.

    Python Figure Reference: layout.sliders -active

    ここではactive=0にして初期状態はy0に設定した。

  • # レイアウトの作成
    layout = go.Layout(
        font_size=20,  # グラフ全体のフォントサイズ
        hoverlabel_font_size=20,  # ホバーのフォントサイズ
        yaxis_range=(0, 10),  # 縦軸の表示範囲
        sliders=sliders  # スライダーを設置
    )
    

    作成したslidersはレイアウト関連なので、go.Figureの引数layoutに直接記述するか、今回のようにgo.Layoutを使ってグラフレイアウトで設定する。

  • # グラフの表示
    fig = go.Figure(data=plot, layout=layout)
    fig.show()
    
    # グラフ保存
    prefix = 'go-slider'  # 保存ファイル名の接頭辞
    save_name = f"{prefix}_add_slider"
    pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
    pio.write_html(fig, f"{save_name}.html")
    pio.write_image(fig, f"{save_name}.png")
    

    あとは作成したデータとレイアウトをgo.Figureに入れてグラフを表示・保存するだけ。ここら辺は他のグラフ表示・保存と同じ。

お気づきの人もいるかもしれないが、実はスライダー機能は同じくPloltyの特徴的な機能のボタンと仕組みが同じ。ただ単にスライダーという形かボタンという形かという違いだけだ。

ボタンの場合はデータやレイアウトの切り替え、スライダーは同系列のデータの切り替えのイメージが強いので必要に応じて使い分けてほしい。

なおPlotlyのボタン機能については以下の記事で詳しく解説している。

【Plotly&ボタン】updatemenusとbuttonsでボタン機能を追加

Plotlyはプロットしたデータを動かすことができるのが大き ...

続きを見る

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# グラフにスライダーを設置

x = np.arange(5)
y0 = 0 * x
y1 = 1 * x
y2 = 2 * x

# 配列の要素にプロット内容を格納
plot = [
    go.Scatter(x=x, y=y0),
    go.Scatter(x=x, y=y1),
    go.Scatter(x=x, y=y2),
]

# y0表示用のステップ作成
step0 = dict(
    label=0,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, False, False]),  # y0のみ表示
        dict(title='y = 0x'),  # グラフタイトル
    ]
)
# y1表示用のステップ作成
step1 = dict(
    label=1,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, True, False]),  # y1のみ表示
        dict(title='y = 1x'),  # グラフタイトル
    ]
)
# y2表示用のステップ作成
step2 = dict(
    label=2,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, False, True]),  # y2のみ表示
        dict(title='y = 2x'),  # グラフタイトル
    ]
)

# スライダーをひとまとめにする
steps = [step0, step1, step2]

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=0,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue=dict(prefix='傾き = '),
        steps=steps  # 作成したスライダー
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    yaxis_range=(0, 10),  # 縦軸の表示範囲
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_add_slider"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

プロットの色を固定し常に凡例を表示する


スライダーを作成できたが現状だと各ステップに動かすとで初期の青色が適用され、さらに凡例が非表示になる。これは1プロットだけになったためPlotlyが自動で調節するのが原因。

ということで、上のグラフではy0, y1, y2それぞれのグラフの色を固定し、さらにわかりやすくなるようにプロット名をつけ凡例を常に表示できるようにした。それぞれのプロット点は以下のように定義。

  • y0: ’red’
  • y1: ’blue’
  • y2: ’green’

go.Scatterのプロット点の色の変更は以下の3種類のどの方法を使ってもいい。今回は3番目の方法を使った。

marker=dict(color='red')
marker={'color': 'red'}
marker_color='red'

また、showlegend=Trueにすることで常に凡例を表示するようになる。これでスライダーを動かしても色は変わらず凡例は表示し続ける。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# プロットの色を固定し凡例を常に表示

x = np.arange(5)
y0 = 0 * x
y1 = 1 * x
y2 = 2 * x

# 配列の要素にプロット内容を格納
# showlegend=Trueで常に凡例を表示するように
plot = [
    go.Scatter(x=x, y=y0, name='y0', marker_color='red', showlegend=True),
    go.Scatter(x=x, y=y1, name='y1', marker_color='blue', showlegend=True),
    go.Scatter(x=x, y=y2, name='y2', marker_color='green', showlegend=True),
]

# y0表示用のステップ作成
step0 = dict(
    label=0,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, False, False]),  # y0のみ表示
        dict(title='y = 0x'),  # グラフタイトル
    ]
)
# y1表示用のステップ作成
step1 = dict(
    label=1,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, True, False]),  # y1のみ表示
        dict(title='y = 1x'),  # グラフタイトル
    ]
)
# y2表示用のステップ作成
step2 = dict(
    label=2,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, False, True]),  # y2のみ表示
        dict(title='y = 2x'),  # グラフタイトル
    ]
)

# スライダーをひとまとめにする
steps = [step0, step1, step2]

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=0,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue=dict(prefix='傾き = '),
        steps=steps  # 作成したスライダー
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    yaxis_range=(0, 8),  # 縦軸の表示範囲
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_add_slider_fix"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

グラフ表示時に初期グラフだけ表示


ただ、現状のままだとグラフの初期表示に全てのプロットが表示された状態で誤解を生む。スライダーを動かせば1プロットずつ表示されるがこれだと面倒だ。

そこで以下の2ステップでグラフの初期表示時には最初(0番目)のプロットのみを表示するように修正した。

  1. go.Scatterの引数visible=Falseでまずは非表示にする
  2. 初期表示したいプロットだけvisible=Trueに変更する

もちろんgo.Scatterでプロットを作成する時点でvisible=Trueにしてもいいが、プロット数が増えた際に条件分岐するのが面倒だったりするので、一旦visible=Falseで非表示にした。

go.Scatterで作成されるプロット(plot)はlistの中にdictがネストされているイメージなので、plot[0][’visilbe’]の形式でvisibleを取り出すことができる。

# 0番目(y = 0x)のグラフだけ、後からvisible=Trueにして初期表示
plot[0]['visible'] = True

一旦Falseにして後で必要な部分だけTrueにする方法はかなり便利なので機会にマスターしてほしい。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# グラフ作成時に初期グラフだけ表示

x = np.arange(5)
y0 = 0 * x
y1 = 1 * x
y2 = 2 * x

# 配列の要素にプロット内容を格納
# showlegend=Trueで常に凡例を表示するように
plot = [
    go.Scatter(
        x=x, y=y0, name='y0',
        marker_color='red', showlegend=True,
        visible=False  # 全グラフを非表示
    ),
    go.Scatter(
        x=x, y=y1, name='y1',
        marker_color='blue', showlegend=True,
        visible=False  # 全グラフを非表示
    ),
    go.Scatter(
        x=x, y=y2, name='y2',
        marker_color='green', showlegend=True,
        visible=False  # 全グラフを非表示
    ),
]
# 0番目(y = 0x)のグラフだけ、後からvisible=Trueにして初期表示
plot[0]['visible'] = True

# y0表示用のステップ作成
step0 = dict(
    label=0,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, False, False]),  # y0のみ表示
        dict(title='y = 0x'),  # グラフタイトル
    ]
)
# y1表示用のステップ作成
step1 = dict(
    label=1,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, True, False]),  # y1のみ表示
        dict(title='y = 1x'),  # グラフタイトル
    ]
)
# y2表示用のステップ作成
step2 = dict(
    label=2,  # スライダーのラベル
    method='update',  # スライダーの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, False, True]),  # y2のみ表示
        dict(title='y = 2x'),  # グラフタイトル
    ]
)

# スライダーをひとまとめにする
steps = [step0, step1, step2]

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=0,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue=dict(prefix='傾き = '),
        steps=steps,  # 作成したスライダー
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    title='y = 0x',  # 初期表示時のグラフタイトル
    yaxis_range=(0, 8),  # 縦軸の表示範囲
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_initial_plot"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

forを使ってスライダー処理を簡略化


ここまでy0, y1, y2のプロットをそれぞれ別で作成したが、このままだと大量のプロットを作成する際にいちいちy100というように書くハメになる。それは避けたいのでforループで描く方法も紹介する。

以下ではプロットの色を配列colorsに入れ、これを基準にしてforループを作成して変数plotを作成。さらにこの変数plotを使って各ステップstepsを作成した。あとの処理はこれまでと同様。

3プロット程度ではforを使う方がむしろ長くなるが、同じようなコードを何回も書くのはナンセンスなのでforを使いたい。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# forを使ってスライダーを作成

x = np.arange(5)

# プロットの色
colors = ('red', 'blue', 'green')
# プロットデータの作成
plot = []
for num, color in enumerate(colors):
    data = go.Scatter(
        x=x, y=num * x, name=f"y{num}",
        marker_color=color, showlegend=True,
        visible=False  # 全グラフを非表示
    )
    plot.append(data)
# 0番目(y = 0x)のグラフだけ、後からvisible=Trueにして初期表示
plot[0]['visible'] = True

# スライダーの内容作成
steps = []
for num, _ in enumerate(plot):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示に変更
    visible = [False] * len(plot)
    visible[num] = True

    step = dict(
        label=num,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # y0のみ表示
            dict(title=f"y = {num}x"),  # グラフタイトル
        ]
    )
    steps.append(step)

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=0,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue=dict(prefix='傾き = '),
        steps=steps  # 作成したスライダー
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    yaxis_range=(0, 8),  # 縦軸の表示範囲
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_add_slider_for"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

一部のプロットを常に表示


場合によっては一部のプロットを常に表示したいというシーンもあるだろう。ここでは1プロットだけを常に表示する方法を紹介する。

といっても先ほどのvisible=True初期表示時に表示するグラフを選ぶという内容を別のプロットでもすればいいだけ。具体的には以下。今回は2番目のプロットを常に表示する。

  • plot[2]['visible'] = Trueで初期表示時にプロットを表示
  • ステップ作成時のvisible[2] = Trueで2番目のプロットを表示

スライダーの各ステップで表示するプロットはvisible=Trueなので、常に表示したいならどのステップでもvisible=Trueとなるようにすればいい。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# 一部のグラフを常に表示

x = np.arange(5)

# プロットの色
colors = ('red', 'blue', 'green', 'orange', 'black')
# プロットデータの作成
plot = []
for num, color in enumerate(colors):
    data = go.Scatter(
        x=x, y=num * x, name=f"y{num}",
        marker_color=color, showlegend=True,
        visible=False  # 全グラフを非表示
    )
    plot.append(data)
# 0番目(y = 0x)のグラフを初期表示
plot[0]['visible'] = True
# 2番目(y = 2x)のグラフも初期表示
plot[2]['visible'] = True
# yの最大値を取得
y_max = max(plot[-1]['y'])

# スライダーの内容作成
steps = []
for num, _ in enumerate(plot):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示に変更
    visible = [False] * len(plot)
    # 各スライダーで表示するプロット設定
    visible[num] = True
    # 2番目(y = 2x)のグラフだけ常に表示するようにする
    visible[2] = True

    step = dict(
        label=num,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # スライダーで選んだプロットだけ表示
            dict(title=f"y = {num}x"),  # グラフタイトル
        ]
    )
    steps.append(step)

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=0,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue=dict(prefix='傾き = '),
        steps=steps,
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    title='y = 0x',  # 初期表示時のグラフタイトル
    yaxis_range=(0, y_max),  # 縦軸の表示範囲
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_always_visible"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

複数プロットを同時に表示・非表示


また、複数のプロットを同時に表示・非表示にしたい場合もあるだろう。この時も表示したいプロットでvisible=Trueを設定したらいい。複数プロットの場合は同じステップで複数のvisible=Trueを設定するだけ。

ここでは合計10プロットで0, 1番目、2, 3番目といったように隣り合ったプロットを同時に表示・非表示になるように設定した。隣同士なのでvisiblenumnum=1のように+1したインデックスでTrueとすればいい。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# 複数プロットを同時に表示・非表示

x = np.arange(5)

# プロットの色
colors = (
    'red', 'blue', 'green',
    'orange', 'black', 'deeppink',
    'purple', 'olive',
    'deepskyblue', 'crimson'
)
# プロットデータの作成
plot = []
for num, color in enumerate(colors):
    data = go.Scatter(
        x=x, y=num * x, name=f"y{num}",
        marker_color=color, showlegend=True,
        visible=False  # 全グラフを非表示
    )
    plot.append(data)
# 0番目(y = 0x)、1番目(y = 1x)のグラフを初期表示
plot[0]['visible'] = True
plot[1]['visible'] = True
# yの最大値を取得
y_max = max(plot[-1]['y'])

# 何データごとに表示・非表示にするか
split = 2  # 2データずつ表示したい
# プロット数と分割数からスライダーの数を計算
nums = int(np.ceil(len(plot) // split))

steps = []
for num in range(nums):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示に変更
    visible = [False] * len(plot)
    # 0番目と1番目、2番目と3番目という風に指定
    slider_num = split * num
    visible[slider_num] = True
    visible[slider_num + 1] = True

    step = dict(
        label=f"{slider_num}, {slider_num+1}",  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # y0のみ表示
            # グラフタイトル
            dict(title=f"y = {slider_num}x, {slider_num + 1}x"),
        ]
    )
    steps.append(step)

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=0,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue=dict(prefix='傾き = '),
        steps=steps,
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    title='y = 0x',  # 初期表示時のグラフタイトル
    yaxis_range=(0, y_max),  # 縦軸の表示範囲
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_2plot_visible"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

一定期間だけプロットを非表示に


visibleを応用することで一定期間(一定条件下のステップ)だけプロットを非表示にするといったことも可能。以下では15~20番目のステップではプロットを非表示にするように設定した。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# 一定範囲だけ非表示に

x = np.arange(30)

# プロットデータの作成
plot = []
for num in range(len(x)):
    data = go.Scatter(
        x=x, y=num * x, name=f"y{num}",
        showlegend=True,
        visible=False  # 全グラフを非表示
    )
    plot.append(data)
# yの最大値を取得
y_max = max(plot[-1]['y'])

# 0番目(y = 0x)のグラフだけ、後からvisible=Trueにして初期表示
plot[0]['visible'] = True

# スライダーの内容作成
steps = []
for num, _ in enumerate(plot):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示に変更
    visible = [False] * len(plot)
    # 15~20番目は非表示
    if (15 <= num) and (num <= 20):
        visible[num] = False
        title = f"y = {num}x: invisible"
    else:
        visible[num] = True
        title = f"y = {num}x: visible"

    step = dict(
        label=num,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # スライダーで選んだプロットだけ表示
            dict(title=title),  # グラフタイトル
        ]
    )
    steps.append(step)

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=0,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue=dict(prefix='傾き = '),
        steps=steps,
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    title='y = 0x',  # 初期表示時のグラフタイトル
    yaxis_range=(0, y_max),  # 縦軸の表示範囲
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_range_plot"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

複数種のデータでスライダーを作成


ここまでは一次関数を例にグラフを作成したが、一次関数単体だと1種類のデータプロットで応用が利きづらい。ということで、複数種類のデータでもスライダーを作成する。

使用するデータはスライダーのグラフではメジャーなsin関数とcos関数で、sin(ax)のaの部分をスライダーで変更するグラフを作成する。

複数種類のデータとなるので再度、各処理を分解しながらグラフ作成を追うことにする。

  • 横軸用のxを作成

    # 0~2πまで用意
    x = np.linspace(0, 2, 301) * np.pi
    

    今回は三角関数のsin関数とcos関数なのでπを基準にxを作成する。0から2πがsin(x), cos(x)の1周期なのでxもこれにならって0~2πで作成。データは301分割と細かめにした。

  • データの作成

    # 1番目だけめだけ初期表示
    sin_data, cos_data = [], []
    # sin(9x). cons(9x)まで作成
    for num in range(10):
        sin_y = np.sin(num * x)
        sin_trace = go.Scatter(
            x=x, y=sin_y, name=f"sin({num}x)",
            visible=False
        )
        cos_y = np.cos(num * x)
        cos_trace = go.Scatter(
            x=x, y=cos_y, name=f"cos({num}x)",
            visible=False
        )
        sin_data.append(sin_trace)
        cos_data.append(cos_trace)
    
    # 1番目だけ表示
    sin_data[1]['visible'] = True
    cos_data[1]['visible'] = True
    plot = sin_data + cos_data
    

    sin関数とcos関数を作成。ここではsin(0x),. cos(0x)からsin(9x), cos(9x)までデータを作成した。初期表示はsin(1x)とcos(1x)(sin(x)とcos(x))がわかりやすいので1番目が表示されるようvisibleは1番目をTrueにした。

    複数種類のデータとしても基本は1種類のデータと同じようにgo.Scatterでプロットを作成すればいい。

    作成したデータは変数plotにsin関数→cos関数の順番に格納した。この順番が次のステップで重要になる。

  • スライダーの各ステップを作成

    # スライダーの内容作成
    steps = []
    for num in range(len(sin_data)):
        # 一旦全てのプロットを非表示にしてから、該当するプロットを表示に変更
        visible = [False] * len(plot)
        visible[num] = True  # sinデータの分
        visible[num + len(sin_data)] = True  # cosデータの分
    
        step = dict(
            label=num,  # スライダーのラベル
            method='update',  # スライダーの適用範囲はデータプロットとレイアウト
            args=[
                dict(visible=visible),  # スライダーで選んだプロットだけ表示
                dict(title=f"sin({num}x), cos({num}x)"),  # グラフタイトル
            ]
        )
        steps.append(step)
    

    続いてスライダーの各ステップを作成する。ここで注意することがあり、1つ前のステップで作成したデータを入れた変数plotのデータの順番とvisibleの順番が連動する点が。

    すでに1種類のデータでのスライダー作成で気づいていた人もいるかもしれないが、順番を意識してvisibleを設定しないと意図しないプロットが表示・非表示される。

    今回はsin関数→cos関数の順番で変数plotgo.Scatterのデータを入れたので、visibleでcos分を編集する際はsin分ずらす必要がある。

    visible[num] = True  # sinデータの分
    visible[num + len(sin_data)] = True  # cosデータの分
    

    もちろんデータ作成時にsin(0x), cos(0x), sin(1x), cos(1x), sin(2x), cos(2x)のように隣り合わせで作成してもいいが、ここではわかりやすさのためにsinとcosのデータはひとまとまりで作成した。

  • スライダーを作成しレイアウトに配置

    # レイアウトに設置するためのスライダー作成
    sliders = [
        dict(
            active=1,  # 初期状態で表示するプロットのインデックス
            # 各スライダーの傾きをスライダーの上に表示
            currentvalue_visible=False,
            steps=steps,
        )
    ]
    
    # レイアウトの作成
    layout = go.Layout(
        font_size=20,  # グラフ全体のフォントサイズ
        hoverlabel_font_size=20,  # ホバーのフォントサイズ
        title='sin(1x), cos(1x)',  # 初期表示時のグラフタイトル
        yaxis_range=(-3, 3),
        sliders=sliders  # スライダーを設置
    )
    

    あとはこれまでと同じように作成した各ステップからスライダーを作成してレイアウトに設置すればいいが、初期表示をsin(x), cos(x)にしたので引数active=1にする必要がある。

    仮にactive=0にするとグラフ表示時のスライダーの位置はsin(0x), cos(0x)なのに表示しているプロットがsin(x), cos(x)で誤解を招くことになる。

    また、グラフ下部の各ステップのラベルはcurrentvalue_visible=Falseで非表示にした。あってもいいがグラフタイトルでここは賄うので削除。

  • # グラフの表示
    fig = go.Figure(data=plot, layout=layout)
    fig.show()
    
    # グラフ保存
    prefix = 'go-slider'  # 保存ファイル名の接頭辞
    save_name = f"{prefix}_multiple_plot"
    pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
    pio.write_html(fig, f"{save_name}.html")
    pio.write_image(fig, f"{save_name}.png")
    

    あとはgo.Figureでグラフを作成し表示・保存するだけ。ここは他のグラフと同じだ。

データの種類が増えてもグラフ作成の本質は同じなので、やり方さえわかればあとはどの順番でvisibleを設定するかだけだ。

なお、sin(9x), cos(9x)のように値の変化が激しいプロットの場合、プロット数が少ないとカクカクになる。データ数を増やせば滑らかになる一方で、コード実行が重くなるので注意が必要だ。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# 複数種のデータでスライダー

# 0~2πまで用意
x = np.linspace(0, 2, 301) * np.pi

# 1番目だけめだけ初期表示
sin_data, cos_data = [], []
# sin(9x). cons(9x)まで作成
for num in range(10):
    sin_y = np.sin(num * x)
    sin_trace = go.Scatter(
        x=x, y=sin_y, name=f"sin({num}x)",
        visible=False
    )
    cos_y = np.cos(num * x)
    cos_trace = go.Scatter(
        x=x, y=cos_y, name=f"cos({num}x)",
        visible=False
    )
    sin_data.append(sin_trace)
    cos_data.append(cos_trace)

# 1番目だけ表示
sin_data[1]['visible'] = True
cos_data[1]['visible'] = True
plot = sin_data + cos_data

# スライダーの内容作成
steps = []
for num in range(len(sin_data)):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示に変更
    visible = [False] * len(plot)
    visible[num] = True  # sinデータの分
    visible[num + len(sin_data)] = True  # cosデータの分

    step = dict(
        label=num,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # スライダーで選んだプロットだけ表示
            dict(title=f"sin({num}x), cos({num}x)"),  # グラフタイトル
        ]
    )
    steps.append(step)

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=1,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue_visible=False,
        steps=steps,
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    title='sin(1x), cos(1x)',  # 初期表示時のグラフタイトル
    yaxis_range=(-3, 3),
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_multiple_plot"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

複数種のデータで一部のデータを常に表示


最後に複数種類のデータのグラフで一部のプロットを常に表示したままにする方法を紹介する。ここでは先ほどのsin関数とcos関数に加え、一次関数をグラフに追加してこの一次関数を常に表示する。

今回はgo.Scatterで作成したプロットをまとめる変数plotの一番初めに一次関数のプロットy_traceを加えることにする。

# 0~2πまで用意
x = np.linspace(0, 2, 301) * np.pi

# 固定用のデータ
y = x
y_trace = go.Scatter(
    x=x, y=y, name='y'
)

plot = [y_trace] + sin_data + cos_data

スライダーのvisibleの順番はgo.Figuredata部分の順番と一致する必要があると書いたので、一番初めに加えたのならvisibleの0番目は常にTrueにする必要がある。

いろいろな方法が考えられるが、ここでは各ステップのsin関数とcos関数のvisibleを作成した配列の始めに一次関数用に[True]を加えることにする。これならシンプルに記述できわかりやすい。

import numpy as np
import plotly.graph_objects as go
import plotly.io as pio

# 複数種のデータで一部のデータを常に表示

# 0~2πまで用意
x = np.linspace(0, 2, 301) * np.pi

# 固定用のデータ
y = x
y_trace = go.Scatter(
    x=x, y=y, name='y'
)

# 1番目だけめだけ初期表示
sin_data, cos_data = [], []
# sin(9x). cons(9x)まで作成
for num in range(10):
    sin_y = np.sin(num * x)
    sin_trace = go.Scatter(
        x=x, y=sin_y,
        name=f"sin({num}x)", visible=False
    )
    cos_y = np.cos(num * x)
    cos_trace = go.Scatter(
        x=x, y=cos_y,
        name=f"cos({num}x)", visible=False
    )
    sin_data.append(sin_trace)
    cos_data.append(cos_trace)

# 1番目だけ表示
sin_data[1]['visible'] = True
cos_data[1]['visible'] = True
# 0番目にyを追加しておく
plot = [y_trace] + sin_data + cos_data

# スライダーの内容作成
steps = []
for num in range(len(sin_data)):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示に変更
    visible = [False] * len(plot)
    visible[num] = True  # sinデータの分
    visible[num + len(sin_data)] = True  # cosデータの分

    # 一次関数y_traceを常に表示するために[True]を追加
    visible = [True] + visible

    step = dict(
        label=num,  # スライダーのラベル
        method='update',  # スライダーの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # スライダーで選んだプロットだけ表示
            dict(title=f"y, sin({num}x), cos({num}x)"),  # グラフタイトル
        ]
    )
    steps.append(step)

# レイアウトに設置するためのスライダー作成
sliders = [
    dict(
        active=1,  # 初期状態で表示するプロットのインデックス
        # 各スライダーの傾きをスライダーの上に表示
        currentvalue_visible=False,
        steps=steps,
    )
]

# レイアウトの作成
layout = go.Layout(
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    title='y, sin(1x), cos(1x)',  # 初期表示時のグラフタイトル
    yaxis_range=(-3, 7),
    sliders=sliders  # スライダーを設置
)
# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフ保存
prefix = 'go-slider'  # 保存ファイル名の接頭辞
save_name = f"{prefix}_multiple_plot_visible"
pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca'
pio.write_html(fig, f"{save_name}.html")
pio.write_image(fig, f"{save_name}.png")

ボタンとともにスライダーもマスターする

ということで今回はPlotlyのスライダー機能を使ってデータを切り替える方法を解説した。すでにPlotlyのボタン機能(updatemenus)を知っている人は気づいただろうが、スライダー機能とボタン機能はかなり似ている。

なのでボタン機能の仕組み・構造が理解できればスライダーの構造も自然と理解できるだろう。

まだPlotlyのボタン機能を理解できていない人は下記のボタン作成の基礎とボタンを再度押した際の動作を追加する方法の記事を参考にしてほしい。

【Plotly&ボタン】updatemenusとbuttonsでボタン機能を追加

Plotlyはプロットしたデータを動かすことができるのが大き ...

続きを見る

【Plotly&ボタン】updatemenusのargs2で2回目のボタン押下機能を追加

今回はPlotlyのボタン機能に2回目のボタン押下の処理を追加& ...

続きを見る

ここら辺のデータの扱い方がわかればPlotlyは途端に理解しやすくなる。是非ともマスターしてほしい。

関連記事

【Plotlyで散布図】go.Scatterのグラフの描き方まとめ

これからPloltyで動くグラフを作りたい、もっとグラフをキ ...

続きを見る

【Pythonを独学】社会人が1人で学習できるのか。結論、学べるが...

今回は社会人がプログラミング言語「Python」を独学で学習 ...

続きを見る

 

Pythonを効率的に学びたいなら

本記事を読んでもっとPythonを学びたいと思った人もいるだろう。ただ、どうせ学ぶなら効率的に学びたい。

就職するにも転職するにも教養として身に付けたいにしろ、遠回りして学習するのはもったいない。

独学でもなんとかなるが

正直、Pythonの学習は独学でもある程度なんとかなると思う。というより他の言語も独学でなんとかなるのが現実。ただ、なんとかなるとしても以下の問題は見逃せない。

  • プログラミングをやらなくても正直問題ない
  • 何をしたらいいのかわからない
  • どう学習したらいいのかわからない
  • これで合っているのかわからない
  • 時間が足りない

私は大学・大学院の研究でPythonを使用、この時に教えてくれる人がいなかったので独学でPythonを学んで大学院の修士論文が通った。

ただ、あくまでもそれは、

  • 大学の研究の過程で必要
  • 何をしないといけないか明白
  • 大学の研究室が超ホワイト
  • 大学生だから時間があった

という「学生」という身分だから独学で学べただけ。すでに社会人の人は時間がないからかなりキツい。

さらに現在大学生の人も大学の授業や研究室が忙くてなかなか独学が難しいという人もいる。

特に社会人は難しい

以下の記事でも解説したが、特に社会人は日々の仕事に追われプライベートが疎かになりがちだ。そこでさらにプログラミングを学ぼうとするとかなりハードルが高い。

今の生活を振り返って自力で独学で学ぶ覚悟と根気と時間があるか確認してほしい。

【Pythonを独学】社会人が1人で学習できるのか。結論、学べるが...

今回は社会人がプログラミング言語「Python」を独学で学習 ...

続きを見る

スクールだと目的と目標がはっきりする

また、プログラミングを学習したいけど何をしたらいいのかわからない、どうしたらいいのかわからないという漠然とした不安がある人も多いだろう。

そんな人はプログラミングスクールに通うことをおすすめする。スクールだと、

  • 何をすればいいのかわかる
  • したいことがなくてもアドバイスをもらえる
  • 学習方法を教えてもらえる

といったメリットがある。何をするかが分かれば最初の一歩は踏み出しやすい。最初が踏み出せないから今立ち止まっているのだから。

さらに独学とは違いそれなりに費用がかかるので、サボるとかなりもったいない。

スクールで客観的視点を持つ

また、現役でエンジニアとして働く私からいうと、独学で学んだコードは我流で実務では使いづらいことも多い。

このサイトで紹介するコードはあくまでも最初の学習をメインとしているから簡素にしているが、実務ではしっかりと汎用性や保守性を担保しないといけない。

そんな中で独学で突っ走ると転職するにも就職するにもその後がかなりしんどい。

プログラミング学習のショートカットはスクール

結論、独学でPythonを学ぶことは可能。しかし効率的かつ汎用的なスキルを身に付けたいならスクールに通うのが1番だ。特に未経験者はなおさら。

私はプログラミング経験があることで転職で若手なのに年収が数十万円も増えたしプログラミングの知識があることで周りから一目置かれることも。

Pythonに限らずだがプログラミングに興味があれば是非ともトライしていただきたい。

  • この記事を書いた人
  • 最新記事

メガネ

ベンチャー企業のWebエンジニア駆け出し。独学のPythonで天文学系の大学院を修了→転職→今に至る。最近は主にPHPを触ってます。 メインのブログは「M天パ」https://megatenpa.com/です。

-Plotly全般の知識
-, , ,