カテゴリー

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

Plotly全般

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

2021年4月13日

こんな人にオススメ

plotlyってグラフ内にボタンをつけて、そのボタンでプロットを表示したりできるらしいじゃん。

どうやったら実装できる?

ということで、今回はplotlyのボタン機能を実装する方法を、goを使用して解説する。ボタンを使用すると簡単にグラフの表示・非表示やグラフの見た目を変更可能。

例えば以下の記事ではボタンを使用してデータ・グラフの切り替えを行なっている。

【ノイキャン対応の完全ワイヤレスイヤホンレビュー】レーダーチャート、棒グラフで比較

続きを見る

【plotly&legendまとめ】凡例の引数一覧

続きを見る

ボタン機能の基本的な考え方は、予めボタンに対応するグラフの表示・非表示を組み込んで、ボタンを押したら呼び出すというもの。ボタンを押すごとに計算しているわけではない。

なので、一回グラフを作成して保存してしまえば、オフラインでも使用できる。

python環境は以下。

  • Python 3.10.1
  • numpy 1.21.4
  • plotly 5.4.0
  • plotly-orca 3.4.2
  • webcolors 1.11.1

なお、新しく立ち上げたサイト「Pro天パ」ではリライト(書き直し)した記事を公開している。併せてご覧いただきたい。

運営者のメガネです。YouTubeTwitterInstagram、自己紹介はこちら、お問い合わせはこちらから。

運営者メガネ

作成したコード全文

下準備(import

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

まずは下準備としてのimport関連。numpyはデータを作成する際に使用。goでグラフを作成し、pioでグラフを保存。goで散布図を描く方法、pioでグラフを保存する方法については以下参照。

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

続きを見る

plotlyでグラフを静止画として保存する際に出るポップアップ
【plotly&orca】plotlyで静止画保存(orca)

続きを見る

最後のwebcolors’red’’blue’といったカラーコードを、(255, 0, 0)(0, 0, 255)といったRGB形式に変換するためのライブラリ。不透明度を変更する際に使用するが、そもそもRGBを使用するなら必要ない。

ボタンなしのグラフ


まずはボタンなしのシンプルなグラフを作成。単に2種類のプロットを1つのグラフに並べた。今回はgo.Scaterで散布図を作成したが、goの散布図については以下参照。

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

続きを見る

2種類のプロット程度なら同時にプロットしても問題ない。予めグループを設定しておき、それらを切り替えたいってなるとボタン機能があるとわかりやすい。

ということで、次からplotlyでボタンを設置する方法を解説する。

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

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

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

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

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20  # ホバーのフォントサイズ
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons'
save_name = f"{prefix}_wide_df"
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")

グラフにボタンを設置


上のグラフでは上部にボタンを設置してy1, y2の表示を切り替えられるように設定した。ボタンの内容は以下。

  • all: y1, y2どちらも表示
  • y1: y1のみ表示
  • y2: y2のみ表示

ボタンを押すことで上記の操作を可能にしているが、その作り方は以下の4ステップで構成されている。今回はわかりやすいようにステップごとにバラバラにコードを書いたが、layoutupdatemenusに直接、全ての処理に入れても問題ない。

  1. 各ボタン(all, y1, y2)の内容を設定: button_al, button1, button2
  2. 使用するボタンを全て配列にまとめる: buttons
  3. レイアウトにボタンを設置するための設定を行う: updatemenus
  4. グラフレイアウトの引数updatemenusに上記設定を適用: updatemenus

これらのステップで作成したコードが以下。次より上記ステップの解説を行う。

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

# 2つのグラフを切り替えられるボタンを作成

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

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

# 2プロット表示用のボタン作成
button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)
# y1表示用のボタン作成
button1 = dict(
    label='y1',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, False]),  # y1のみ表示
        dict(title='y1 plot'),  # グラフタイトル
    ]
)
# y2表示用のボタン作成
button2 = dict(
    label='y2',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, True]),  # y2のみ表示
        dict(title='y2 plot'),  # グラフタイトル
    ]
)

# ボタンをひとまとめにする
buttons = [button_all, button1, button2]

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=0,  # 初期グラフで見た目上、押されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons'
save_name = f"{prefix}_simple_buttons"
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")

各ボタン(all, y1, y2)の内容を設定

# 2プロット表示用のボタン作成
button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)
# y1表示用のボタン作成
button1 = dict(
    label='y1',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, False]),  # y1のみ表示
        dict(title='y1 plot'),  # グラフタイトル
    ]
)
# y2表示用のボタン作成
button2 = dict(
    label='y2',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, True]),  # y2のみ表示
        dict(title='y2 plot'),  # グラフタイトル
    ]
)

まずはボタンの内容。主に使用する引数は以下。

  • label: ボタンに表示されるボタンラベル
  • method: クリック時にどの部分を変更するか
    • 'restyle' : プロットだけ変更
    • 'relayout': レイアウトだけ変更
    • 'animate': アニメーションにする
    • 'update': プロットとレイアウト両方を変更
    • 'skip': 変更しない
  • args: クリック時の動作
    • 1つ目のdictがプロット関連
    • 2つ目のdictがレイアウト関連

labelはボタンラベルなのでわかりやすい名称にすると良い。method’update’を指定しておけば、アニメーションにしない限りは問題ない。goのアニメーションについては以下。

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

続きを見る

argsは2つのdictを格納したlistで指定する。1つ目のdictがプロット関連の内容で、ここではvisibleとすることでグラフの表示・非表示を指定している。Trueが表示、Falseが非表示だ。

このvisibleはプロットを作成した時の順番と同じ位置で指定する。ここではy1y2の順番でプロットを作成したので、button1[True, False]より、y1を表示してy2を非表示にする。

2つ目のdictはレイアウト関連の設定を入れる。ここではグラフタイトルを設定した。

使用するボタンを全て配列にまとめる

# ボタンをひとまとめにする
buttons = [button_all, button1, button2]

これに関してはシンプルに、作成したボタンたちを1つの配列にまとめるだけ。今回は3つのボタンを作成したのでこれらをひとまとめにした。

レイアウトにボタンを設置するための設定を行う

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=0,  # 初期グラフで見た目上、押されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

続いてはレイアウトに設置するための設定。各ボタンではなく、ボタン全体としてレイアウトにどのように設置するかを決める。主な引数は以下。

  • active: グラフ作成時に見た目上どのボタンが押されているか
  • type: ボタンタイプ。buttonsdropdownから選択
  • direction: ボタンを並べる方向。'left', 'right', 'up', 'down’より選択
  • x, y: ボタンの位置
  • xanchor, yanchor: x, yの基準。x=1, xanchor=’right’ならボタン右端がx=1にくる
  • buttons: 作成したボタンはここへ
  • pad: 余白。t, b, l, rと数値で上下左右の余白を設定

activeはグラフの初期表示時にどのボタンが見た目の上で押されているかを決める。これはあくまでも見た目上の話で、例えばactive=1y1のボタンが押されるようにするとおかしなことになる。

なぜなら、go.Scatter作成時に全プロットを表示するようにしているから、ボタンはy1が押されているのに見えているプロットはy1, y2となる。チグハグ。

なお、active=-1に設定すると初期表示では見た目の上ではボタンは押されないようになる。

グラフレイアウトの引数updatemenusに上記設定を適用

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    updatemenus=updatemenus,  # ボタンを設置
)

最後に作成したボタン設定をレイアウトに反映させる。引数はupdatemenus。今回はボタン設定の変数もupdatemenusで作成したので、updatemenus=updatemenusとする。

その他の引数についてはコメントアウトの通り。グラフタイトルとフォントサイズを変更している。

ドロップダウンのボタンを作成


続いてはボタン形式をドロップダウンにした場合のグラフだ。ドロップダウンにするには上で書いた変数updatemenusの引数typetype=’dropdown’にするだけ。

なお、direction=’right’のままだと右側に出てきて押しづらかったので、type=’down’にして下方向に展開されるように設定した。

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

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

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

# 2プロット表示用のボタン作成
button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)
# y1表示用のボタン作成
button1 = dict(
    label='y1',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, False]),  # y1のみ表示
        dict(title='y1 plot'),  # グラフタイトル
    ]
)
# y2表示用のボタン作成
button2 = dict(
    label='y2',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[False, True]),  # y2のみ表示
        dict(title='y2 plot'),  # グラフタイトル
    ]
)

# ボタンをひとまとめにする
buttons = [button_all, button1, button2]

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=-1,  # -1にすると初期グラフでは見た目上ボタンは押されない
        type='dropdown',  # ボタンのタイプはドロップダウン
        direction='down',  # ボタンは下向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons'
save_name = f"{prefix}_simple_buttons_dropdown"
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を使用してボタンを作成

できるグラフは同じだが、for文を使ってボタンを作成することももちろん可能。for文を使用することで、複数ボタンも短く書くことができる。先程の例だと3ボタンだったが、これが5ボタンとかになると面倒になるから楽をする。

なお、y1, y2はボタンのコードが似ているのでここをfor文で書く。一方で、allボタンはコードの構造が少々異なるので、ここで統一感を出そうとするよりも初めから分けたほうが見やすくなる。

forを使用する場合は以下のようにvisibleを操作する。

  1. 一旦、全プロット数だけのFalseを持つlist visibleを作成
  2. forループで表示したいプロットに該当するvisible内のFalseTrueに変更
  3. 編集したvisibleをボタンに適用

以上を踏まえて作成したコードが以下。forplotenumerateで数値numを取得しながら回している。numは何番目のvisibleTrueにするかという処理で使用。num=0y1, num=1y2に対応している。

また、plotの各要素dataprint(plot)で出力される各go.Scatterのこと。go.Scatterdictのように扱うことができるので、data[’name’]とすることでプロット名(y1, y2)を取り出せる。

仮にxが欲しかったらdata[’x’]にするとよいし、マーカーの色が欲しかったらdata[’marker’][’color’]にするとよい。

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

# forを使って短く描く

x = (0, 1, 2, 3, 4)
y1 = (0, 1, 4, 3, 2)
y2 = (1, 3, 2, 4, 0)

# 配列の要素にプロット内容を格納
plot = [
    go.Scatter(x=x, y=y1, name='y1'),
    go.Scatter(x=x, y=y2, name='y2'),
]
print(plot)
# [Scatter({
#     'name': 'y1', 'x': [0, 1, 2, 3, 4], 'y': [0, 1, 4, 3, 2]
# }), Scatter({
#     'name': 'y2', 'x': [0, 1, 2, 3, 4], 'y': [1, 3, 2, 4, 0]
# })]

# 2プロット表示用のボタン作成
button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)
# 2プロット表示用のボタンを先に入れる
buttons = [button_all]

# forを使ってボタン作成を短く書く
for num, data in enumerate(plot):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示変更
    visible = [False] * len(plot)
    visible[num] = [True]

    # ボタン作成
    button = dict(
        label=data['name'],  # ボタンのラベル
        method='update',  # ボタンの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # y1のみ表示
            dict(title=f"{data['name']} plot"),  # グラフタイトル
        ]
    )
    buttons.append(button)

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=0,  # 初期グラフで見た目上、押されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons'
save_name = f"{prefix}_simple_buttons_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")

5データのグラフで少し応用


やっていることは大差ないが、ここではデータ数を増やして合計5データとした。今回はy = ax + bの直線のグラフのうち、bの部分を増やしてグラフを作成。

先程のy1, y2のグラフからの変更点は以下。

  • marker_colorプロットの色を指定
  • sholegend=Trueでボタン操作で1プロットになっても凡例が消えないように
  • xaxis_range, yaxis_rangeで横軸・縦軸の表示範囲を固定
  • legend=dict(...)で凡例をプロット領域の右端に移動

先程のy1, y2にグラフだとボタンを押してy1, y2だけにすると、凡例が消えるしプロットの色も変更された。今回はこれを防ぐために凡例の常時表示と色指定をした。

また、グラフの表示範囲を固定することで、プロットが変わった時の自動表示範囲変更を防いでいる。そして蛇足だが凡例の位置も変更した。

なお、ボタンはfor文を使って短く作成。ボタンが合計で6つもあるのでforで短く書きたい。

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

x = np.arange(5)
ys = {}
for num in range(5):
    y = x + num  # y = ax + bの形
    ys[f"x + {num}"] = y
print(ys)
# {'x + 0': array([0, 1, 2, 3, 4]), 'x + 1': array([1, 2, 3, 4, 5]), 'x + 2': array([2, 3, 4, 5, 6]), 'x + 3': array([3, 4, 5, 6, 7]), 'x + 4': array([4, 5, 6, 7, 8])}

# 配列の要素にプロット内容を格納
plot = []
colors = ('red', 'blue', 'green', 'orange', 'black')
for num, (name, y) in enumerate(ys.items()):
    d = go.Scatter(
        x=x, y=y, name=name,
        marker_color=colors[num],  # プロット線の色
        showlegend=True  # 凡例を常に表示
    )
    plot.append(d)

# 全プロット表示用のボタン作成
button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)
# 全プロット表示用のボタンを先に入れる
buttons = [button_all]

# forを使ってボタン作成を短く書く
for num, data in enumerate(plot):
    # 一旦全てのプロットを非表示にしてから、該当するプロットを表示変更
    visible = [False] * len(plot)
    visible[num] = [True]

    # ボタン作成
    button = dict(
        label=data['name'],  # ボタンのラベル
        method='update',  # ボタンの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # y1のみ表示
            dict(title=f"y = {data['name']}"),  # グラフタイトル
        ]
    )
    buttons.append(button)

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=0,  # 初期グラフで見た目上、押されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    xaxis_range=(-1, 5),  # 横軸の表示範囲
    yaxis_range=(-1, 9),  # 縦軸の表示範囲
    legend=dict(x=1, xanchor='right'),  # 凡例のx位置をプロット領域の右端に
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons'
save_name = f"{prefix}_5data"
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プロットずつボタンで操作していた。しかし、複数プロットを同時に操作したい時もあるだろう。そんな時はvisibleTrueの箇所を増やしたらいい。

上のグラフではbutton1x + 0, x + 1button2x + 2, x + 3button3x+4の表示に対応している。

今回は変数visiblesで各プロットの表示・非表示を決めている。このvisibledictで作成することで、for使用時にlabelvisibleを同時に回せる。

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

x = np.arange(5)
ys = {}
for num in range(5):
    # np.random.seed(num)
    # y = np.random.rand(10)
    y = x + num  # y = ax + bの形
    ys[f"x + {num}"] = y
print(ys)
# {'x + 0': array([0, 1, 2, 3, 4]), 'x + 1': array([1, 2, 3, 4, 5]), 'x + 2': array([2, 3, 4, 5, 6]), 'x + 3': array([3, 4, 5, 6, 7]), 'x + 4': array([4, 5, 6, 7, 8])}

# 配列の要素にプロット内容を格納
plot = []
colors = ('red', 'blue', 'green', 'orange', 'black')
for num, (name, y) in enumerate(ys.items()):
    d = go.Scatter(
        x=x, y=y, name=name,
        marker_color=colors[num],  # プロット線の色
        showlegend=True  # 凡例を常に表示
    )
    plot.append(d)

# 全プロット表示用のボタン作成
button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        dict(visible=[True, True]),  # y1, y2の両方を表示
        dict(title='all plots'),  # グラフタイトル
    ]
)
# 全プロット表示用のボタンを先に入れる
buttons = [button_all]

# 該当するプロットの位置をTrueにすると表示になる
visibles = dict(
    button1=[True, True, False, False, False],  # x+0, x+1
    button2=[False, False, True, True, False],  # x+2, X+3
    button3=[False, False, False, False, True]  # x+4
)

# visiblesをforで回す
for label, visible in visibles.items():
    # ボタン作成
    button = dict(
        label=label,  # ボタンのラベル
        method='update',  # ボタンの適用範囲はデータプロットとレイアウト
        args=[
            dict(visible=visible),  # y1のみ表示
            dict(title=label),  # グラフタイトル
        ]
    )
    buttons.append(button)

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=0,  # 初期グラフで見た目上、押されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    xaxis_range=(-1, 5),  # 横軸の表示範囲
    yaxis_range=(-1, 9),  # 縦軸の表示範囲
    legend=dict(x=1, xanchor='right'),  # 凡例のx位置をプロット領域の右端に
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons'
save_name = f"{prefix}_5data_multiple"
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. allボタンでは、プロットの色を元の色にする
  2. 各プロットのボタンでは、該当プロットの不透明度以外を0.1にする

上のグラフを作成するためのコートは以下。次より重要そうな部分を解説する。

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

# ボタンを押すとプロットの不透明度が変わるグラフ

# redを(R, G, B)に変換
red = webcolors.name_to_rgb('red')
print(red)
# IntegerRGB(red=255, green=0, blue=0)
print(tuple(red))
# (255, 0, 0)

x = np.arange(5)
ys = {}
for num in range(5):
    # np.random.seed(num)
    # y = np.random.rand(10)
    y = x + num  # y = ax + bの形
    ys[f"x + {num}"] = y
print(ys)
# {'x + 0': array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), 'x + 1': array([ 1,  2,  3,  4,  5,  6,  7,  8,  9, 10]), 'x + 2': array([ 2,  3,  4,  5,  6,  7,  8,  9, 10, 11]), 'x + 3': array([ 3,  4,  5,  6,  7,  8,  9, 10, 11, 12]), 'x + 4': array([ 4,  5,  6,  7,  8,  9, 10, 11, 12, 13]), 'x + 5': array([ 5,  6,  7,  8,  9, 10, 11, 12, 13, 14]), 'x + 6': array([ 6,  7,  8,  9, 10, 11, 12, 13, 14, 15]), 'x + 7': array([ 7,  8,  9, 10, 11, 12, 13, 14, 15, 16]), 'x + 8': array([ 8,  9, 10, 11, 12, 13, 14, 15, 16, 17]), 'x + 9': array([ 9, 10, 11, 12, 13, 14, 15, 16, 17, 18])}

# 配列の要素にプロット内容を格納
plot = []
colors = ('red', 'blue', 'green', 'orange', 'black')
for num, (name, y) in enumerate(ys.items()):
    d = go.Scatter(
        x=x, y=y, name=name,
        marker_color=colors[num],  # プロット線の色
        showlegend=True  # 凡例を常に表示
    )
    plot.append(d)

# 全プロット表示用のボタン作成
color_lst = []
# 各プロットを1つずつ調べる
for num, data in enumerate(plot):
    # 各プロットの色を抽出
    color = data['marker']['color']
    color_lst.append(color)

button_all = dict(
    label='all',  # ボタンのラベル
    method='update',  # ボタンの適用範囲はデータプロットとレイアウト
    args=[
        {'marker.color': color_lst},  # プロットの色を変更
        dict(title='all plots'),  # グラフタイトル
    ]
)
# 全プロット表示用のボタンを先に入れる
buttons = [button_all]

# forを使ってボタン作成を短く書く
for num, data in enumerate(plot):
    color_lst = []
    alphas = [0.1] * len(plot)  # 一旦全てのプロットの不透明度を0.1にする
    alphas[num] = 1  # 該当するプロットの不透明度を1にする

    # 各プロットを1つずつ調べる
    for num_, data_ in enumerate(plot):
        # 各プロットの色を抽出
        color = data_['marker']['color']
        # 色の名称を(R, G, B)の形式に変換
        rgb = tuple(webcolors.name_to_rgb(color))
        # 末尾に不透明度をつけて(R, G, B, a)形式に
        rgba = rgb + (alphas[num_],)
        color = f"rgba{rgba}"
        # 先頭に文字列rgnaをつけrgba(R, G, B, a)形式に
        color_lst.append(color)

    # ボタン作成
    button = dict(
        label=data['name'],  # ボタンのラベル
        method='update',  # ボタ ンの適用範囲はデータプロットとレイアウト
        args=[
            {'marker.color': color_lst},  # プロットの色を変更
            dict(title=f"y = {data['name']}"),  # グラフタイトル
        ]
    )
    buttons.append(button)

# レイアウトに設置するためのupdatemenus作成
updatemenus = [
    dict(
        active=0,  # 初期グラフで見た目上、押されるボタン
        type='buttons',  # ボタンのタイプはボタンに
        direction='right',  # ボタンは右向きに配置
        x=0.5, y=1.01,  # ボタンの位置
        xanchor='center', yanchor='bottom',  # ボタンの位置の基準
        buttons=buttons,  # ここに設定したボタン情報を入れる
    )
]

# レイアウトの作成
layout = go.Layout(
    title='title',  # グラフタイトル
    font_size=20,  # グラフ全体のフォントサイズ
    hoverlabel_font_size=20,  # ホバーのフォントサイズ
    xaxis_range=(-1, 5),  # 横軸の表示範囲
    yaxis_range=(-1, 9),  # 縦軸の表示範囲
    legend=dict(x=1, xanchor='right'),  # 凡例のx位置をプロット領域の右端に
    updatemenus=updatemenus,  # ボタンを設置
)

# グラフの表示
fig = go.Figure(data=plot, layout=layout)
fig.show()

# グラフの保存
prefix = 'plotly-buttons'
save_name = f"{prefix}_5data_alpha"
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")

色指定のdict{}で指定

allボタンではgo.Scatterで作成した色を使用。色はredなどWebカラー(CSSカラー)で指定しているので、不透明度は1となる。要するに全プロットを濃くする。

色を変更するにはplotの各データdataにおいてdata[’marekr’][’color’]で指定可能。また、ボタンでマーカーの色を指定する際にはdict{}で括った方法じゃないといけない。以下のように指定するとエラーとなる。

dict(marker.color=color_lst),
         ^^^^^^^^^^^^^
SyntaxError: expression cannot contain assignment, perhaps you meant "=="?

webcolorでCSSカラーをRGBへ

一方で、各ボタンでは一旦、全プロットの不透明度を0.1に変更、そのあとボタンに該当するプロットの不透明度を1に戻す。

なお、redなどのCSSカラーでは不透明度を変更することができない。したがって、色をRGBに変換できるライブラリwevcolorを使用する。これでred→(255, 0, 0)と変換可能。

これに不透明度を最後にくっつけ(255, 0, 0, 0.1)とする。ただ、これだけだとうまく色が反映されない。実は文頭にrgbaという文字をつけないといけない。つけると完成。

# forを使ってボタン作成を短く書く
for num, data in enumerate(plot):
    color_lst = []
    alphas = [0.1] * len(plot)  # 一旦全てのプロットの不透明度を0.1にする
    alphas[num] = 1  # 該当するプロットの不透明度を1にする

    # 各プロットを1つずつ調べる
    for num, data in enumerate(plot):
        # 各プロットの色を抽出
        color = data['marker']['color']
        # 色の名称を(R, G, B)の形式に変換
        rgb = tuple(webcolors.name_to_rgb(color))
        # 末尾に不透明度をつけて(R, G, B, a)形式に
        rgba = rgb + (alphas[num],)
        color = f"rgba{rgba}"
        # 先頭に文字列rgnaをつけrgba(R, G, B, a)形式に
        color_lst.append(color)

あとは今までと同じようにボタンを作成してupdatemenusを設定したらいい。

ボタンで切り替え自由

ということで、今回はplotlyupdatemenusを使用してグラフにボタンを追加する方法を解説した。

同様のグラフを作成する際にボタンで切り替えられるようにしたらhtmlファイルで切り替えられるから後からも便利。

しかも、以下の記事で書いたようにpltでhtml形式で保存するよりもplotlyのhtmlの方が自由度が高いし便利。

【plt&mpld3】pltで作ったグラフを後から編集できるようにhtmlで保存

続きを見る

なのでplotlyのボタンを活用できるようにして色々なグラフを作成していただければ幸いだ。

なお、ボタンをもう一度押した時の挙動を指定することも可能。これについては以下の記事で書いているので参照してほしい。

【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加

続きを見る

関連記事

【plotly&ボタン】グラフに複数種のボタンを追加

続きを見る

【plotly&buttons】ボタンを押すとプロット・背景の色が変わる変なグラフ

続きを見る

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

続きを見る

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

続きを見る

【plotly&fill】goで領域を塗りつぶし

続きを見る

bubblechart2017_pop
【plotly&バブルチャート】plotlyで各国の収入と平均寿命をバブルチャートで描く

続きを見る

【plotly&棒グラフ】go.Barでバーチャートを作成

続きを見る

【plotly&rangeslider/rangeselector】レンジスライダーとレンジセレクターで時系列を見やすく

続きを見る

ガジェット

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

-Plotly全般
-, , ,