本記事では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
を使用する。pio
はplotly
でのグラフ保存用のライブラリ。保存の仕方は色々あるがpio
はその1つだ。
スライダーなしのグラフ
まずはスライダーなしのプロットから。使うのはgo.Scatter
でできるプロット(グラフ)は散布図だ。go.Scatter
の詳しい引数やグラフの作成方法については下記の記事参照。
-
【Plotlyで散布図】go.Scatterのグラフの描き方まとめ
これからPloltyで動くグラフを作りたい、もっとグラフをキ ...
続きを見る
プロットは3種類のy=axの形式の一次関数。これらをそれぞれy0
, y1
, y2
として定義し変数plot
にgo.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
:レイアウト属性の変更update
:restyle
とrelayout
の両方の変更が可能animate
:アニメーションの作成
args
:各ステップの動作内容(下記はmethod=update
の時)- 1つ目の
dict
:データ関連の処理 - 2つ目の
dict
:レイアウト関連の処理
これらの他にも多数の引数があるが、ここでは基礎的な内容として最小限に留めておく。詳しくは公式ドキュメント参照。
今回のスライダーでは
method=update
に設定し、args
の1つ目のdict
でvisible
を設定した。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番目)のプロットのみを表示するように修正した。
go.Scatter
の引数visible=False
でまずは非表示にする- 初期表示したいプロットだけ
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番目といったように隣り合ったプロットを同時に表示・非表示になるように設定した。隣同士なのでvisible
はnum
とnum=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関数の順番で変数
plot
にgo.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.Figure
のdata
部分の順番と一致する必要があると書いたので、一番初めに加えたのなら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」を独学で学習 ...
続きを見る