こんな人にオススメ
plotly
ってグラフの切り替えとかできるボタンをつけられるけど、これって複数種可能?
複数の処理をボタンを分けて行いたい。
ということで、今回はplotly
で作ったグラフに複数種のボタンを追加する方法について解説する。以下の記事でグラフにボタンを追加するということを解説した。
-
-
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
続きを見る
-
-
【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加
続きを見る
これらのグラフでは1種類のボタンを作成し、さらに後者では2回押した時の挙動を設定した。が、あくまでも1種類のボタン。本記事の方法を使用すれば、複数種のボタンを作成し、用途を分けられるようになる。
じゃあどうすれば複数種のボタンを作成できるのかというと、updatemenus
を複数作成してそれらを結合してgo.Layout
に入れる。以下で具体的な内容を解説する。
python環境は以下。
- Python 3.9.6
- numpy 1.21.1
- matplotlib 3.4.2
- plotly 5.1.0
- plotly-orca 3.4.2
下準備
import sys import numpy as np import matplotlib.cm as cm import plotly import plotly.graph_objects as go import plotly.io as pio sys.path.append('../../') import plotly_layout_template as template
まずは下準備としてのimport
関連。plotly_layout_template
は自作テンプレートの入っているファイルで、本記事のコードの2つ上のディレクトリに置いているため'../../'
で戻っている。詳細は以下参照。
-
-
【随時更新 備忘録】plotlyのグラフ即席作成コード
続きを見る
使用するデータ
x = np.arange(10) ys = {} for num in range(5): y = x + num ys[num] = y
今回使用するデータはシンプルな直線。これに0から4までの整数を足すことで1ずつ上にズラしている。中身を見てみると以下。
for name, y in ys.items(): print(f"name: {name}, y: {y}") # name: 0, y: [0 1 2 3 4 5 6 7 8 9] # name: 1, y: [ 1 2 3 4 5 6 7 8 9 10] # name: 2, y: [ 2 3 4 5 6 7 8 9 10 11] # name: 3, y: [ 3 4 5 6 7 8 9 10 11 12] # name: 4, y: [ 4 5 6 7 8 9 10 11 12 13]
go.Scatter
作成や表示切り替えの関数を定義
まずはグラフを作成する際に使用する関数を定義する。必ずしも定義しなければいけないわけではないが、各処理を分けられるのでスッキリする。
今回は以下の5項目について予め定義する。後に解説する、もう少しややこしいグラフに使う関数はまた後ほど定義する。ここでは汎用的なものだけを解説する。
- プロットデータの作成
- プロットの表示・非表示の切り替えボタン作成
- 作成したボタンをグラフに配置
- グラフレイアウトの作成
- 作成したグラフの保存
関数については以下参照。
-
-
【python&関数化】defとかargsとかを使って関数を作成する
続きを見る
go.Scatter
のプロットデータの作成
def scatter(x, y, name, color): d = go.Scatter( mode='lines+markers', x=x, y=y, name=name, marker_color=color, hovertemplate=f"{name}<br>" + 'x: %{x} <br>' + 'y: %{y} <br>' + "<extra></extra>", ) return d
引数x
, y
でプロットの横・縦の値を、name
でそのプロットの名称を、そしてcolor
でそのプロットの色を設定する。他にも設定できる項目はあるが、最低限必要なものはこれくらい。
プロットは線と点を表すmode='lines+markers'
で、hovertemplate
でマウスオーバー時の表示項目を設定している。
プロット点の表示切り替えボタンの作成
def set_visible_buttons(labels: list, start=0): # ボタンの内容を作成 buttons = [] for num, label in enumerate(labels, start): # 一旦全てのプロットを非表示に visible = [False] * len(ys) if num: # それぞれのプロットを表示するとき visible[num - 1] = True else: # 全てのプロットを表示するとき visible = [True] * len(ys) button = dict( label=label, method='update', args=[dict(visible=visible), dict(title=label), ] ) buttons.append(button) return buttons
押したボタンに対応したプロットを表示してその他を非表示にする設定を行う関数。labelsにボタンに書くラベルデータを入れて、そのラベル1つひとつに対して設定を行う。
今回は全プロットを表示するALL
ボタンも追加しているので少しややこしいが、num
が0
の時はALL
になるように設定している。詳しくは以前のボタンの記事参照。
ボタンをグラフに配置する関数の作成
def set_updatemenus(x, y, buttons, button_type='buttons', direction='right'): updatemenus = [ dict( type=button_type, direction=direction, x=x, y=y, xanchor='center', yanchor='bottom', active=0, buttons=buttons, ) ] return updatemenus
ボタンは作成するだけでは適用できなくて、それをグラフに配置しないといけない。そのための処理を行う関数がset_updatemenus
関数。引数x
, y
にボタンを置く位置を、buttons
に作成したボタンのデータを入れる。
button_type
が'buttons'
の場合は設定したボタンが並び、'dropdown'
にするとボタンを押すと内容一覧が出てくるようになる。direction
はボタンを置く方向。
レイアウトデータの作成
def set_layout(title: str, updatemenus: list): layout = go.Layout( template=template.plotly_layout(), title=dict(text=title,), xaxis=dict(title='x',), yaxis=dict(title='y', range=(-1, 15)), updatemenus=updatemenus, ) return layout
グラフのレイアウト設定を行う関数。引数template
で自作のテンプレートを読み込んでいる。引数title
にグラフタイトルを入れ、さらに引数updatemenus
に先程のset_updatemenus
関数の返り値を入れることで、グラフにボタンを追加可能。
また、yaxis
のrange
でグラフの表示範囲を設定しているが、この設定をしないとset_visible_buttons
関数で特定のグラフだけ表示した時に縦軸の範囲が変わってしまう。
グラフの保存処理の作成
def save(fig, name: str): pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html( fig, f'{name}.html', auto_open=False, config=template.plotly_config() ) pio.write_image(fig, f'{name}.png')
最後に作成したグラフの保存処理を関数化。今回はhtmlとpng形式で保存するが、pdfでもjpegでも可能。グラフ保存については以下で格闘の記録を残しているので参照。
-
-
【plotly&orca】plotlyで静止画保存(orca)
続きを見る
シンプルにプロットの表示を切り替える
まずはシンプルに、ボタンを押すとそのボタンに対応したプロットだけが表示されるグラフを作成。上のグラフでいうと「0
」を押すと直線データに+0
したプロットのみが、「ALL
」を押すと全プロットが表示される。
このグラフに関しては以下の記事で詳しく解説している。プロットデータは違えど根本的な処理内容は変わらない。また、書き方も人それぞれだから仕組みだけわかればいい。
-
-
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
続きを見る
ボタンに表示するラベルを作成
labels = ['ALL'] + list(ys) print(labels) # ['ALL', 0, 1, 2, 3, 4]
ボタンにはそれぞれを判別するためのラベルが必要だが、今回はys
で定義したkeys
をラベルとした。加えて、全グラフを表示するためのボタンとして'ALL'
も作成した。
ラベルは何回も使用することもあるので、予め定義しておいた方が楽。
グラフを作成
def single_button(): plot = [] for num, y in ys.items(): color = cm.jet(num / len(ys)) color = plotly.colors.convert_to_RGB_255(color) color = f"rgb{color}" d = scatter(x=x, y=y, name=num, color=color) plot.append(d) # ボタンに使用するラベルを作成 buttons = set_visible_buttons(labels=labels) # 作成したボタンをグラフに配置 updatemenus = set_updatemenus(x=0.5, y=1.01, buttons=buttons) # レイアウトの設定 layout = set_layout(title=labels[0], updatemenus=updatemenus) fig = go.Figure(data=plot, layout=layout) fig.show(config=template.plotly_config()) # グラフの保存 name = 'single_button' save(fig=fig, name=name) single_button()
多くの処理を予め関数として定義したので、実際にグラフを描く際のコードはかなり短くなる。色はplotly
のカラースケールよりもmatplotlib
の方が使い勝手がいいのであえてmatplotlib
を使用。
あとは各関数に必要な引数を入れてあげれば良い。関数を予め定義するのは面倒かもしれないが、最終的にやりたい処理がスッキリ見えるからなかなか侮れない。
実際にできるグラフが本章始めのグラフで、ボタンを押すことでプロットの切り替えができる。
2つのボタンでプロットの表示を切り替える
本題の複数種ボタンの作成だが、まずは2種類から。本記事を見ている環境によっては上のグラフのボタンが重なるかもしれないが、実際にグラフを作成するといい感じのボタン配置になるはず。
このグラフでは1つ目のグラフのALL
, 0
-4
のボタンを2グループに分けた。わざわざ分ける必要はないけど、パッと思いついた方法がコレ。何か系統の異なるデータを使用している時はこんな感じで作成すると良いだろう。
複数のボタンを作成にはupdatemenus
を結合
def double_buttons(): plot = [] for num, y in ys.items(): color = cm.jet(num / len(ys)) color = plotly.colors.convert_to_RGB_255(color) color = f"rgb{color}" d = scatter(x=x, y=y, name=num, color=color) plot.append(d) # プロット線切り替えボタン作成 # ALLと0 buttons1 = set_visible_buttons(labels=labels[0:2], start=0) updatemenus1 = set_updatemenus(x=0.5, y=1.01, buttons=buttons1) # 1, 2, 3, 4 buttons2 = set_visible_buttons(labels=labels[2:6], start=2) updatemenus2 = set_updatemenus(x=0.5, y=1.05, buttons=buttons2) # ボタンは結合して1つにしておく updatemenus = updatemenus1 + updatemenus2 print(updatemenus) # レイアウトの設定 layout = set_layout(title=labels[0], updatemenus=updatemenus) fig = go.Figure(data=plot, layout=layout) fig.show(config=template.plotly_config()) # グラフの保存 name = 'double_buttons' save(fig=fig, name=name) double_buttons()
既に書いたが、複数種のボタンを作成するには、複数ボタンに対応したupdatemenus
を結合する。まず、上のコードではupdatemenus1
でALL
, 0
を、updatemenus2
で1
, 2
, 3
, 4
を担当している。
これらupdatemenus1
, 2
を結合し、結合したものをlayout
の引数に入れるだけ。結構シンプル。ただし、結合する際に無駄にlist
が入っていたりするとエラー。
エラーと正常の場合のupdatemenus
の値を以下に示す。エラーの場合は例えばupdatemenus
の結合を次のようにした場合。無駄にlistが入ってエラーの原因に。
updatemenus = [] updatemenus.append(updatemenus1) updatemenus.append(updatemenus2)
updatemenus
を結合することでできるグラフが上のグラフというわけだ。
2つ目のボタンでlog
とlinear
を切り替え
プロットの表示・非表示はできたので、ここでは軸のタイプを変更する方法について解説する。具体的には、縦軸を線形(linear)と対数(log)に変更するボタンを追加する。
今回の例ではあまり効果は出ないが、実際にlinearスケールで書いたグラフをlogにしたい時はボタン1つで切り替えができるので楽。このグラフでも記事に載せるとボタンが被るかもしれないが、グラフ化するといい感じになる。
ボタンのレイアウト部分に変更内容を書く
def set_log_buttons(): # ボタンの内容を作成 buttons = [] # logとlinearで表示範囲を指定する range_dct = {'linear': (-1, 15), 'log': (np.log10(0.9), np.log10(15))} for y_type in range_dct: button = dict( label=y_type, method='update', args=[ dict(), # dict(yaxis=dict(type=y_type, range=range_dct[y_type],),), # の代わりに以下尿に「.」を使用することができる {'yaxis.type': y_type, 'yaxis.range': range_dct[y_type], }, ] ) buttons.append(button) return buttons
今回はプロット点に対する処理ではなく、レイアウトに関する処理なのでさっきと勝手が異なる。ボタンにレイアウトの操作を入れるにはargs
の2つ目のdict
を編集すればいい。
既にプロットの表示・非表示でグラフタイトルの設定で使用したが、ここにyaxis
のタイプをlogとlinearに変更する内容を書けばいい。
注意するべき点が、plotly
では対数の軸範囲制限(range
)をする際にはnp.log10((軸の値))と
しなければいけないという点。シンプルに15と入れると軸の値が$10^{15}$になって悲惨なことになる。
縦軸のタイプを変更するグラフ
def double_buttons_log(): plot = [] for num, y in ys.items(): color = cm.jet(num / len(ys)) color = plotly.colors.convert_to_RGB_255(color) color = f"rgb{color}" d = scatter(x=x, y=y, name=num, color=color) plot.append(d) # プロット線切り替えボタン作成 buttons1 = set_visible_buttons(labels=labels, start=0) updatemenus1 = set_updatemenus(x=0.5, y=1.01, buttons=buttons1) # logとlienar切り替え用のボタンを作成 buttons2 = set_log_buttons() updatemenus2 = set_updatemenus(x=0.5, y=1.05, buttons=buttons2) # ボタンは結合して1つにしておく updatemenus = updatemenus1 + updatemenus2 # レイアウトの設定 layout = set_layout(title=labels[0], updatemenus=updatemenus) fig = go.Figure(data=plot, layout=layout) fig.show(config=template.plotly_config()) # グラフの保存 name = 'double_buttons_log' save(fig=fig, name=name) double_buttons_log()
縦軸のタイプを変更するグラフを作成するためのコードは上。今までのものと遜色ないようなグラフになっている。しかし、これでタイプが変更できたのでより見やすいグラフができるだろう。
プロットの色を変更するボタンを追加
最後に、先ほどの軸タイプボタンに加えて、プロットの色を変更するボタンを追加する。色変更のボタンは今までのbuttons
ではなくdropdown
にしたので、押したら内容一覧が出てくる。
どこで活用するのかと言われると怪しいところもあるけど、こんなこともできるんだくらいに見ていってほしい。
各ボタンでプロットの色を自動作成
colors_scales = { 'jet': cm.jet, 'viridis': cm.viridis, 'plasma': cm.plasma, 'Reds': cm.Reds, 'spring': cm.spring, 'cool': cm.cool, 'Pastel1': cm.Pastel1, 'Set1': cm.Set1, 'prism': cm.prism, }
今更ではあるけど、今回のグラフの色は全てカラースケール(カラーマップ?)を使用している。例えばcm.jet
というカラースケールの0
に該当する色を使用してねという感じ。0
は紺色っぽい色。
で、ここでのボタンはこのカラースケールを変更するというもの。変数colors_scales
で色々とカラースケールを取得したので、コレを使用して色を変更する。
プロット関係なのでargs
の1つ目の要素に書く
def set_color_buttons(colors_scales: dict): buttons = [] for label, color_map in colors_scales.items(): colors = [] for num, _ in enumerate(ys): color = color_map(num / len(ys)) color = plotly.colors.convert_to_RGB_255(color) colors.append(f"rgb{color}") button = dict( label=label, method='update', args=[ {'marker.color': colors}, dict(title=label), ] ) buttons.append(button) return buttons
プロットの色はプロット関係なので、ボタンのargs
の1つ目のdict
に入れる。2つ目のdict
はいつも通りグラフタイトルを入れている。
また、今回はプロットの色ということだが、matplotlib
とplotly
で色の扱い方が異なるので、for num
の後でplotly
の色の扱いに合わせている。色についての詳細は以下。
-
-
【plotly&色の自動調節】入力したRGBをrgba(R, G, B, a)に自動変換する
続きを見る
プロット表示・非表示、軸タイプ変更、プロット色変更グラフ
def double_buttons_color(): plot = [] for num, y in ys.items(): color = cm.jet(num / len(ys)) color = plotly.colors.convert_to_RGB_255(color) color = f"rgb{color}" d = scatter(x=x, y=y, name=num, color=color) plot.append(d) # プロット線切り替えボタン作成 buttons1 = set_visible_buttons(labels=labels, start=0) updatemenus1 = set_updatemenus(x=0.5, y=1.01, buttons=buttons1) # logとlienar切り替え用のボタンを作成 buttons2 = set_log_buttons() updatemenus2 = set_updatemenus(x=0.54, y=1.05, buttons=buttons2) # プロット色を変更するボタン作成 buttons3 = set_color_buttons(colors_scales=colors_scales) updatemenus3 = set_updatemenus( x=0.46, y=1.05, buttons=buttons3, button_type='dropdown', direction='down', ) # ボタンは結合して1つにしておく updatemenus = updatemenus1 + updatemenus2 + updatemenus3 # レイアウトの設定 layout = set_layout(title='jet', updatemenus=updatemenus) fig = go.Figure(data=plot, layout=layout) fig.show(config=template.plotly_config()) # グラフの保存 name = 'double_buttons_color' save(fig=fig, name=name) double_buttons_color()
ということで、本記事で使用したボタンを全部使ったグラフコードが上。プロット表示・非表示、軸タイプ変更、プロット色変更となかなかモリモリの内容。
このコードでできるグラフが本章初めのグラフとなる。見る環境でボタンが重なるのはどうにかして改善したいところ。
複数ボタンを使うことで自由度が上げられる
今回はplotly
のグラフに複数種のボタンを追加する方法について解説した。シンプルにupdatemenus
を結合したらできたので結構びっくりした。
ボタンを使用することでplotly
のグラフの自由度は上がるが、複数種のボタンを使用することで用途を分けることができる。つまり、さらに自由度が増すということだ。
自由度が増すからもっと頭を使うことになるし、煩雑にならないような見た目も気にするようになる。選択の自由と頭の体操、老化予防には効くのかもしれない、知らんけど。
関連記事
続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る
【plotly&go.Scatter】plotlyの散布図グラフの描き方
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
【plotly&config】グラフのツールバーを編集する
【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加
【plotly&スライダー】plotlyのslidersにスライダーを追加
【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く
【plotly&rangeslider/rangeselector】レンジスライダーとレンジセレクターで時系列を見やすく
【plotly&fill】goで領域を塗りつぶし
【plotly&3D】goで3Dグラフを作成
【plotly&マーカー】plotlyのマーカーのシンボル