やりたいこと
- グラフ内にボタンを作成して、ボタンを押すことでプロットの表示/非表示,タイトルなどのレイアウトを操作する
- 表示したプロットに対してスライダーで操作可能にする

環境
- Mac OS
- python 3.8.5
- plotly 4.12.0
pip
pip install plotly
pip install numpy
ボタンのおさらい

import numpy as np
import plotly.offline as offline
import plotly.graph_objs as go
import plotly
# 線の色取得
colors = plotly.colors.DEFAULT_PLOTLY_COLORS
x = np.linspace(-5, 5, 11)
data = []
for i in range(1, 4):
trace = go.Scatter(
x=x,
y=x ** i,
name="y{}".format(i),
mode="lines",
line=dict(color=colors[i], width=6),
visible=False,
showlegend=True
)
data.append(trace)
# 初期状態としてdataの0番目を見えるようにする
data[0].visible = True
"""
ボタン作成個所
キーの説明
active: 初期状態で押されているボタンを指定、buttonsキー値のリストインデックスを入力
type: ドロップダウンかボタンか ( "dropdown" | "buttons" ), default="dropdown"
buttons: ボタンの設定
"""
buttons = []
for ii in range(len(data)):
visible = [False] * len(data) # len(data)=3だから, visible=[False, False, False]
visible[ii] = True # ii番目だけTrueにする, ex) [True, False, False]
button = dict(label=data[ii].name,
method="update", # レイアウトを更新する為"update"
args=[dict(visible=visible), # Trueにした個所のtraceのみ見える
dict(title="button_plotly_{}".format(data[ii].name), # title更新
yaxis=dict(title="yaxis_{}".format(data[ii].name), showspikes=True))]) # y軸ラベル更新
buttons.append(button)
updatemenus = [dict(active=0, type="buttons", buttons=buttons)]
layout = go.Layout(
title="button_plotly_y1",
xaxis=dict(title="xaxis", showspikes=True, domain=[0.05, 1.0]),
yaxis=dict(title="yaxis_y1", showspikes=True),
font=dict(size=16),
updatemenus=updatemenus,
showlegend=True)
fig = dict(data=data, layout=layout)
offline.plot(fig, auto_open=True, include_plotlyjs="cdn", filename=r"./button_plotly.html")
以下がボタンを作成している個所です。
updatemenus = [dict(active=0, type="buttons", "buttons"=buttons)]
の"buttons"=buttons
がボタンを押した時の情報(表示/非表示など)を作成している個所です。
buttons
はリストで、中にはレイアウト情報の辞書(下記ではbutton
変数)が格納されています。
"""
ボタン作成個所
キーの説明
active: 初期状態で押されているボタンを指定、buttonsキー値のリストインデックスを入力
type: ドロップダウンかボタンか ( "dropdown" | "buttons" ), default="dropdown"
buttons: ボタンの設定
"""
buttons = []
for ii in range(len(data)):
visible = [False] * len(data) # len(data)=3だから, visible=[False, False, False]
visible[ii] = True # ii番目だけTrueにする, ex) [True, False, False]
button = dict(label=data[ii].name,
method="update", # レイアウトを更新する為"update"
args=[dict(visible=visible), # Trueにした個所のtraceのみ見える
dict(title="button_plotly_{}".format(data[ii].name), # title更新
yaxis=dict(title="yaxis_{}".format(data[ii].name), showspikes=True))]) # y軸ラベル更新
buttons.append(button)
updatemenus = [dict(active=0, type="buttons", buttons=buttons)]
今回はボタンでプロットの表示/非表示、タイトル、y軸ラベルの切り替えをするように書いています。
visible=[False] * len(data)
visble[ii] = True
はどのプロットを表示するかを設定しています。
例えば、data=[trace1, trace2, trace3]
でvisible=[True, False, False]
の時、trace1のみ表示されます。
最後にgo.Layout(updatemenus=updatemenus)
でグラフにボタンができます。
スライダーのおさらい

import numpy as np
import plotly.offline as offline
import plotly.graph_objs as go
# sin, cos波のtraceをを別々に保存
sin_data, cos_data = [], []
x = np.linspace(0, 10, 101)
for step in np.linspace(0, 5, 51):
y = np.sin(step * x)
y2 = np.cos(step * x)
sin_trace = go.Scatter(
x=x,
y=y,
name="sin {:.1f}Hz".format(step),
line=dict(color="red", width=3),
visible=False, )
cos_trace = go.Scatter(
x=x,
y=y2,
name="cos {:.1f}Hz".format(step),
line=dict(color="blue", width=3),
visible=False, )
sin_data.append(sin_trace)
cos_data.append(cos_trace)
# sin, cos共にデフォルトでindex=10のトレースを表示する
sin_data[10].visible = True
cos_data[10].visible = True
data = sin_data + cos_data
steps = []
"""
sin_data, cos_dataの表示/非表示を別々に設定した後、結合してvisibleキーに追加
例えばスライダーが2の時、
sin_visible = [False, False, True, False,...]
cos_visible = [False, False, True, False,...]
にした後2つを結合することで、sin, cos共にindex=2のtraceを表示できるようにする
"""
for s in range(len(sin_data)):
# sin_data, cos_data共に一度全て非表示にする
sin_visible, cos_visible = [False] * len(sin_data), [False] * len(cos_data)
# それぞれs番目だけ見えるようにする
sin_visible[s], cos_visible[s] = True, True
step = dict(method="update",
args=[{"visible": sin_visible + cos_visible},
{"title": "Simple slider step: {}".format(s)}]
)
steps.append(step)
sliders = [dict(active=10, currentvalue=dict(prefix="Frequency: "), pad=dict(t=50), steps=steps)]
layout = go.Layout(
title="Simple slider step: 10",
xaxis=dict(title="x"),
yaxis=dict(title="y"),
font=dict(size=16),
hovermode='x unified',
hoverlabel=dict(font_size=16),
sliders=sliders,
showlegend=True)
fig = dict(data=data, layout=layout)
offline.plot(fig, include_plotlyjs="cdn", filename="plots.html")
下記でスライダーを作成しています
steps = []
"""
sin_data, cos_dataの表示/非表示を別々に設定した後、結合してvisibleキーに追加
例えばスライダーが2の時、
sin_visible = [False, False, True, False,...]
cos_visible = [False, False, True, False,...]
にした後2つを結合することで、sin, cos共にindex=2のtraceを表示できるようにする
"""
for s in range(len(sin_data)):
# sin_data, cos_data共に一度全て非表示にする
sin_visible, cos_visible = [False] * len(sin_data), [False] * len(cos_data)
# それぞれs番目だけ見えるようにする
sin_visible[s], cos_visible[s] = True, True
step = dict(method="update",
args=[{"visible": sin_visible + cos_visible},
{"title": "Simple slider step: {}".format(s)}]
)
steps.append(step)
sliders = [dict(active=10, currentvalue=dict(prefix="Frequency: "), pad=dict(t=50), steps=steps)]
やっていることはボタンとあまり変わっていません。
sliders = [dict(active=10, currentvalue=dict(prefix="Frequency: "), pad=dict(t=50), steps=steps)]
のsteps=steps
がスライダーを動かした時の情報(表示/非表示など)を作成している個所です。
steps
はリストで、中にはレイアウト情報の辞書(上記ではstep
変数)が格納されています。
最後にgo.Layout(sliders=sliders)
でグラフにスライダーができます。
詳細
plotly スライダーを使ったグラフの応用 - Qiita
サンプルグラフ

3つのボタンで
- sin, cos両方(ALL)
- sinのみ(SIN)
- cosのみ(COS)
の表示を切り替え後、見えているプロットに対してスライダーで周波数を変更します。
スライダーはALL, SIN, COSの3つを作成しておき、ボタンでレイアウトを更新する際にスライダーも入れ替えます。
import copy
import numpy as np
import plotly.offline as offline
import plotly.graph_objs as go
# sin, cos波のtraceをを別々に保存
sin_data, cos_data = [], []
x = np.linspace(0, 10, 101)
for step in np.linspace(0, 5, 51):
y = np.sin(step * x)
y2 = np.cos(step * x)
sin_trace = go.Scatter(
x=x,
y=y,
name="sin {:.1f}Hz".format(step),
line=dict(color="red", width=3),
visible=False, )
cos_trace = go.Scatter(
x=x,
y=y2,
name="cos {:.1f}Hz".format(step),
line=dict(color="blue", width=3),
visible=False, )
sin_data.append(sin_trace)
cos_data.append(cos_trace)
sin_data[10].visible = True
cos_data[10].visible = True
data = sin_data + cos_data
steps = {"ALL": [], "SIN": [], "COS": []}
"""
visible情報のリストは計4つ
sin_visible_false: sin波のvisibleが全てFalse
cos_visible_false: cos波のvisibleが全てFalse
sin_visible_true: sin波のindex=sのvisibleがTrue
cos_visible_true: cos波のindex=sのvisibleがTrue
表示したくないものはFalseにすればいいから、
sin, cos両方表示するときのvisible情報: sin_visible_true + cos_visible_true
sinのみ表示するときのvisible情報: sin_visible_true + cos_visible_false
cosのみ表示するときのvisible情報: sin_visible_false + cos_visible_true
"""
for s in range(len(sin_data)):
# sin_data, cos_data共に一度全て非表示にする
sin_visible_false, cos_visible_false = [False] * len(sin_data), [False] * len(cos_data)
# sin_visible_false, cos_visible_falseのコピー作成、コピーのindex=sに対してTrueにする
sin_visible_true, cos_visible_true = copy.copy(sin_visible_false), copy.copy(cos_visible_false)
sin_visible_true[s], cos_visible_true[s] = True, True
step_all = dict(method="update",
args=[{"visible": sin_visible_true + cos_visible_true},
{"title": "slider_button_ALL step: {}".format(s)}]
)
step_sin = dict(method="update",
args=[{"visible": sin_visible_true + cos_visible_false},
{"title": "slider_button_SIN step: {}".format(s)}]
)
step_cos = dict(method="update",
args=[{"visible": sin_visible_false + cos_visible_true},
{"title": "slider_button_COS step: {}".format(s)}]
)
steps["ALL"].append(step_all)
steps["SIN"].append(step_sin)
steps["COS"].append(step_cos)
sliders = {}
for key, step in steps.items():
slider = [dict(active=10, currentvalue=dict(prefix="Frequency: "), pad=dict(t=50), steps=step)]
sliders[key] = slider
buttons = []
for key, slider in sliders.items():
slider_active = slider[0]["active"]
slider_visible = slider[0]["steps"][slider_active]["args"][0]["visible"]
button = dict(label=key,
method="update",
args=[dict(visible=slider_visible),
dict(title="slider_button_{} step: {}".format(key, slider_active),
yaxis=dict(title="y {}".format(key)),
sliders=slider)])
buttons.append(button)
updatemenus = [dict(active=0, type="buttons", buttons=buttons)]
layout = go.Layout(
title="slider_button_ALL step: 10",
xaxis=dict(title="x", domain=[0.05, 1]),
yaxis=dict(title="y ALL"),
font=dict(size=16),
hovermode='x unified',
hoverlabel=dict(font_size=16),
sliders=sliders["ALL"],
updatemenus=updatemenus,
showlegend=True)
fig = dict(data=data, layout=layout)
offline.plot(fig, auto_open=True, include_plotlyjs="cdn", filename=r"./slider_button_plotly.html",
config={'modeBarButtonsToAdd': ['drawline', 'drawopenpath', 'drawclosedpath', 'drawcircle', 'drawrect',
'eraseshape']})
スライダーを動かした時のプロット表示を作成しています。
steps = {"ALL": [], "SIN": [], "COS": []}
"""
visible情報のリストは計4つ
sin_visible_false: sin波のvisibleが全てFalse
cos_visible_false: cos波のvisibleが全てFalse
sin_visible_true: sin波のindex=sのvisibleがTrue
cos_visible_true: cos波のindex=sのvisibleがTrue
表示したくないものはFalseにすればいいから、
sin, cos両方表示するときのvisible情報: sin_visible_true + cos_visible_true
sinのみ表示するときのvisible情報: sin_visible_true + cos_visible_false
cosのみ表示するときのvisible情報: sin_visible_false + cos_visible_true
"""
for s in range(len(sin_data)):
# sin_data, cos_data共に一度全て非表示にする
sin_visible_false, cos_visible_false = [False] * len(sin_data), [False] * len(cos_data)
# sin_visible_false, cos_visible_falseのコピー作成、コピーのindex=sに対してTrueにする
sin_visible_true, cos_visible_true = copy.copy(sin_visible_false), copy.copy(cos_visible_false)
sin_visible_true[s], cos_visible_true[s] = True, True
step_all = dict(method="update",
args=[{"visible": sin_visible_true + cos_visible_true},
{"title": "slider_button_ALL step: {}".format(s)}]
)
step_sin = dict(method="update",
args=[{"visible": sin_visible_true + cos_visible_false},
{"title": "slider_button_SIN step: {}".format(s)}]
)
step_cos = dict(method="update",
args=[{"visible": sin_visible_false + cos_visible_true},
{"title": "slider_button_COS step: {}".format(s)}]
)
steps["ALL"].append(step_all)
steps["SIN"].append(step_sin)
steps["COS"].append(step_cos)
sliders = {}
for key, step in steps.items():
slider = [dict(active=10, currentvalue=dict(prefix="Frequency: "), pad=dict(t=50), steps=step)]
sliders[key] = slider
slidersの中身は{"ALL":ALLの時のスライダー, "SIN":SINの時のスライダー,...}
となっています
buttons = []
for key, slider in sliders.items():
slider_active = slider[0]["active"]
slider_visible = slider[0]["steps"][slider_active]["args"][0]["visible"]
button = dict(label=key,
method="update",
args=[dict(visible=slider_visible),
dict(title="slider_button_{} step: {}".format(key, slider_active),
yaxis=dict(title="y {}".format(key)),
sliders=slider)])
buttons.append(button)
updatemenus = [dict(active=0, type="buttons", buttons=buttons)]
ボタンを作成しています。
slider_visible = slider[0]["steps"][slider_active]["args"][0]["visible"]
はスライダーの初期位置のvisible情報を取得しています。
上のスクリプトの場合スライダーの初期位置を10としているので、ボタンを切り替えた直後の表示もそれに合わせないとスライダーの位置と表示が合わない為です。
layout = go.Layout(
title="slider_button_ALL step: 10",
xaxis=dict(title="x", domain=[0.05, 1]),
yaxis=dict(title="y ALL"),
font=dict(size=16),
hovermode='x unified',
hoverlabel=dict(font_size=16),
sliders=sliders["ALL"],
updatemenus=updatemenus,
showlegend=True)
fig = dict(data=data, layout=layout)
offline.plot(fig, auto_open=True, include_plotlyjs="cdn", filename=r"./slider_button_plotly.html",
config={'modeBarButtonsToAdd': ['drawline', 'drawopenpath', 'drawclosedpath', 'drawcircle', 'drawrect',
'eraseshape']})
レイアウトを決めて出力しています。
デフォルトはALLを表示するようにしています。
config={...}は右上のお絵かきボタンなどを追加しています。
参考
公式
Custom Buttons | Python | Plotly
layout.updatemenus | Python | Plotly
Plotly Python Graphing Library | Python | Plotly