こんな人にオススメ
plotly
のlayout
の引数legend
で凡例のカスタムをしたいけど、どんな引数がある?
ということで、今回はplotly
で凡例をカスタムする際に使用するlayout
の引数legend
の引数と挙動をまとめた。
matplotlib
、plt
とは異なりplotly
ではカスタムできる項目が少ないけど、plotly
独特のクリックしたときの挙動の変化などを簡単に変更することができる。
なお、Plotly公式の「Legends in Python」でも凡例について解説されている。今回はこの内容を参考に解説する。
python環境は以下。
- Python 3.10.1
- numpy 1.21.4
- pandas 1.3.5
- plotly 5.4.0
- plotly-orca 3.4.2
作成したコード全文
下準備(import
)
import numpy as np import pandas as pd import plotly.graph_objects as go import plotly.express as px import plotly.io as pio
まずは下準備としてのimport
関連。今回は基本的にgo
を使って解説するが、最後にpx
でも可能ということを解説するのでpx
もimport
している。
pio
はグラフ保存用のライブラリ。以下の記事で解説している。
-
-
【plotly&orca】plotlyで静止画保存(orca)
続きを見る
numpy
とpandas
はデータ作成用などに使用する。
シンプルにプロット
まずはシンプルにプロットしてグラフを作成。使用するデータは上下2グループに分けられそうな適当な数列。
このグラフをデフォルトの基準として、以下より色々と引数や設定を変更する。
go
での散布図の描き方の詳細は以下を参照していただくとして、軽く解説する。
-
-
【plotly&go.Scatter】plotlyの散布図グラフの描き方
続きを見る
go
で散布図を描くにはgo.Scatter
が便利。その引数x
に横軸の配列、y
に縦軸の配列、name
に各プロットの名称を入れるだけでプロットが完了。
あとはgo.Figure
でプロット情報を入れた変数plotを引数に入れる。最後にfig.show()
でグラフを表示、pio
でグラフを保存。
なお、デフォルトだとフォントサイズが小さめだったので、fig.update_layout
でフォントサイズを上げている。詳細は以下。
-
-
【plotly&fig作成と更新】add_traceやupdate_layoutの使い方
続きを見る
import plotly.graph_objects as go import plotly.io as pio # シンプルにプロット # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name # プロットの名称 ) plot.append(d) # プロットを格納 # グラフの作成 fig = go.Figure(data=plot) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_simple" 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")
bgcolor
, bordercolor
で凡例の色を変更
レイアウトの引数をいじることで凡例の背景色などを変更可能。ここでは以下の3項目を変更した。
bgcolor
: 背景色bordercolor
: 凡例の枠線の色borderwidth
: 凡例の枠線の太さ
また、フォントサイズについてはfig.update_layout
にしなくても、go.Layout
の引数として直接指定することも可能。
直接go.Layout
に入れた方がスッキリすると思ったので、ここでは直接指定とした。
import plotly.graph_objects as go import plotly.io as pio # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name # プロットの名称 ) plot.append(d) # プロットを格納 # レイアウトの作成 layout = go.Layout( legend=dict( bgcolor='lavenderblush', # 背景色 bordercolor='blue', # 枠線の色 borderwidth=2, # 枠線の太さ ), # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_color" 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")
font_size
, font_family
でフォントのサイズや種類を変更
続いてはフォントやフォントサイズの変更。フォントに関しては凡例に限らず横軸ラベルやグラフタイトル、プロットのホバーなどにも反映させることが可能。
font_family
でフォントの種類を変更
まずはフォントの種類を引数fontのさらに引数familyで変更する。フォントについては使用環境で左右されるかもしれないが、とりあえず下記の15種類を使用することにした。
# 使用するフォントファミリー font_families = ( 'Arial', 'Balto', 'Courier New', 'Droid Sans', 'Droid Serif', 'Droid Sans Mono', 'Gravitas One', 'Old Standard TT', 'Open Sans', 'Overpass', 'PT Sans Narrow', 'Raleway', 'Times New Roman', 'sans-serif', 'verdana', )
また、これらフォントに対して1つのグラフを作成するとなると、かなり多くのグラフができてしまう。
ということでplotly
お得意のボタン機能で各フォントを切り替えられるようにした。ボタンについては以下参照。
-
-
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
続きを見る
今回はフォントの種類のみをボタンで切り替えられるようにするので、レイアウト関連の変更のみとなる。
レイアウトのみの変更を行うボタン機能をつけたグラフの簡単な作成手順は以下。
- プロットを作成(
plot
) - 各ボタン内容(今回はフォントの種類)を作成(
button
,buttons
) - ボタン設定時の
method
はレイアウトの変更のみなので’relayout’
に - レイアウトに設置するための設定を行う(
updatemenus
) go.Layout
にupdatemenus
を入れる
また、フォントの種類が多いので、updatemenus
の引数type
はドロップダウンdropdown
にして普段は隠すようにした。
さらにグラフタイトルについてもついでにフォント変更しておいた。蛇足。
import plotly.graph_objects as go import plotly.io as pio # フォントの種類やサイズを変更 # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # プロットの名称 ) plot.append(d) # プロットを格納 # 使用するフォントファミリー font_families = ( 'Arial', 'Balto', 'Courier New', 'Droid Sans', 'Droid Serif', 'Droid Sans Mono', 'Gravitas One', 'Old Standard TT', 'Open Sans', 'Overpass', 'PT Sans Narrow', 'Raleway', 'Times New Roman', 'sans-serif', 'verdana', ) # font切り替え用のボタン作成 buttons = [] for num, font_family in enumerate(font_families): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=font_family, # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=font_family, font=dict(family=font_family)), legend=dict(font=dict(family=font_family)) ), ] ) buttons.append(button) # レイアウトにボタンを設置するためのupdatemenus作成 updatemenus = [ dict( active=0, # 初期グラフで見た目上、押されるボタン type='dropdown', # ボタンのタイプはボタンに direction='down', # ボタンは下向きに配置 x=0.5, y=1.01, # ボタンの位置 xanchor='center', yanchor='bottom', # ボタンの位置の基準 buttons=buttons, # ここに設定したボタン情報を入れる ) ] # レイアウトの作成 layout = go.Layout( title='Default font', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_font" 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")
font_size
で凡例のフォントサイズを変更
次はフォントサイズを変更する。これもフォントの種類と同じように凡例に限った話ではないが凡例での指定でどれくらいサイズが変わるのかを示しておく。
フォントサイズも1サイズずつグラフを作成すると面倒なので、今度はスライダーで1グラフに収めることにする。スライダーについては以下。
-
-
【plotly&スライダー】plotlyのslidersにスライダーを追加
続きを見る
スライダーの作成手順もボタンと大差ない。それぞれのスライダーの内容を設定(ここではフォントサイズ)して、それをgo.Layout
に反映させるだけ。
引数が異なっていたりするけど、基本的な考え方は同じ。なので、ボタンかスライダーかを理解すればもう片方も理解しやすい。
import plotly.graph_objects as go import plotly.io as pio # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # プロットの名称 ) plot.append(d) # プロットを格納 # 設定するフォントサイズ sizes = (0, 1, 5, 10, 15, 20, 30) # スライダーの作成 steps = [] for size in sizes: step = dict( method='relayout', label=size, args=[ # 各スライダーのグラフタイトルとフォントサイズ dict( title=dict(text=f"font size = {size}", font=dict(size=size)), legend=dict(font=dict(size=size)) ) ], ) steps.append(step) # レイアウトにスライダーを設置するためのsliders作成 sliders = [ dict( active=0, # 初期状態で表示するプロットのインデックス # 各スライダーの傾きをスライダーの上に表示 currentvalue=dict(prefix='font size: '), steps=steps, # スライダー情報 ) ] # レイアウトの作成 layout = go.Layout( title='Default font size', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, sliders=sliders # スライダー設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_fontsize" 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")
valign
で複数行の凡例の時のシンボルの位置を指定
凡例の名称が長すぎて改行して表示した際、凡例のシンボルの縦の位置をどうするか指定するのが引数valign
の役割。
理解しにくい内容かつ、あまり使われないと思うが、上のグラフでボタンを変更してもらうと理解しやすいと思う。
top
にすると凡例の名称の上端にシンボルが、bottom
にすると下端にシンボルが来る。middle
にするとちょうど真ん中にシンボルが来る。
凡例はgo.Scatter
の引数name
で指定した内容が反映される。なので、name
で<br>
タグを使って改行しておいた。使い道は少ない気がする。
import plotly.graph_objects as go import plotly.io as pio # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=f"prefix<br>{name}<br>suffix", # 複数行にしたプロットの名称 ) plot.append(d) # プロットを格納 # シンボルの位置 valigns = ('top', 'middle', 'bottom') # font切り替え用のボタン作成 buttons = [] for num, valign in enumerate(valigns): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=valign, # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=valign), legend=dict(valign=valign) ), ] ) buttons.append(button) # レイアウトにボタンを設置するためのupdatemenus作成 updatemenus = [ dict( active=1, # 初期グラフで見た目上、押されるボタン type='buttons', # ボタンのタイプはボタンに direction='right', # ボタンは右向きに配置 x=0.5, y=1.01, # ボタンの位置 xanchor='center', yanchor='bottom', # ボタンの位置の基準 buttons=buttons, # ここに設定したボタン情報を入れる ) ] # レイアウトの作成 layout = go.Layout( title='Default valign', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_valign" 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")
x
, y
とanchor
で凡例の位置を変更
お次は凡例自体の位置を変更。こちらはわかりやすいだろう。ただし、x
, y
とxanchor
, yanchor
は異なるので注意。
x
, y
はシンプルに凡例の位置。anchor
がつく引数はその位置の基準を上下左右どこにするか。
x
, y
で位置を変更
まずはx
, y
について見てみる。今回は0
, 0.5
, 1
という3種類の位置でグラフを作成した。
import plotly.graph_objects as go import plotly.io as pio # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # プロットの名称 ) plot.append(d) # プロットを格納 # 凡例の位置 position_xs = position_ys = (0, 0.5, 1) # スライダーの作成 steps = [] for position_x in position_xs: for position_y in position_ys: # 凡例を置く座標 coordinates = f"(x, y)=({position_x}, {position_y})" step = dict( method='relayout', label=coordinates, args=[ # 各スライダーのグラフタイトルとフォントサイズ dict( title=dict(text=coordinates,), legend=dict(x=position_x, y=position_y) ) ], ) steps.append(step) # レイアウトにスライダーを設置するためのsliders作成 sliders = [ dict( active=0, # 初期状態で表示するプロットのインデックス steps=steps, # スライダー情報 ) ] # レイアウトの作成 layout = go.Layout( title='Default legend position', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, sliders=sliders # スライダー設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_xy" 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")
xanchor
, yanchor
で位置の基準を指定
anchor
は位置の基準をどこにするかを指定する引数。例えばxanchor
の場合だとx=1
の基準を凡例の右端にするのか左端にするのか中央にするのかって感じ。
上のグラフではxanchor
, yanchor
のボタンを作成した。本当はボタンではlegend
自体の上書きではなくxanchor
だけの変更(更新)という風にしたかったができなかった。
なので、yanchor
を変更するとxanchor
がデフォルトの位置に戻ってしまう。まあ動きを見る分には問題ないか。
import plotly.graph_objects as go import plotly.io as pio # anchorで凡例の位置基準を決める # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # プロットの名称 ) plot.append(d) # プロットを格納 # x, yのanchor xanchors = ("auto", "left", "center", "right") yanchors = ("auto", "top", "middle", "bottom") # xanchor切り替え用のボタン作成 buttons = [] for num, xanchor in enumerate(xanchors): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=f"x_{xanchor}", # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=f"x_{xanchor}"), legend=dict(xanchor=xanchor) ), ] ) buttons.append(button) # yanchor切り替え用のボタン作成 for num, yanchor in enumerate(yanchors): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) right # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=f"y_{yanchor}", # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=f"y_{yanchor}"), legend=dict(yanchor=yanchor) ), ] ) buttons.append(button) # レイアウトにボタンを設置するためのupdatemenus作成 updatemenus = [ dict( active=1, # 初期グラフで見た目上、押されるボタン type='<meta charset='utf-8'>dropdown', # ボタンのタイプはボタンに direction='', # ボタンは右向きに配置 x=0.5, y=1.01, # ボタンの位置 xanchor='center', yanchor='bottom', # ボタンの位置の基準 buttons=buttons, # ここに設定したボタン情報を入れる ) ] # レイアウトの作成 layout = go.Layout( title='Default xanchor, yanchor', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_anchor" 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")
traceorder
とtracegroupgap
で凡例の順番と間隔
plotly
にはlegendgroup
という機能がある。これは複数のプロットをここのプロットではなく、グループとして捉えるというもの。
例えば、data1
, data2
はグループa、data3
, data4
, data5
はグループbという感じ。
このlegendgroup
を使った時の順番とグループ間のギャップ(間隔)を変更する。
traceorder
で凡例の順番を変更
まずは引数traceorder
で凡例の順番を変更。これはグループにしなくても適用できるけど、グループに対応したgrouped
があるからここで解説。
引数は単発でnormal
やreversed
としてもいいが、組み合わせたい時は+
を使うことが可能。
なお、normal
はデフォルトで、reversed
は全プロットを逆順に、grouped
はグループごと分けることを意味する。
import plotly.graph_objects as go import plotly.io as pio # 凡例グループの順番 # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # traceorder='grouped'のためにプロットをグループ化 legendgroup = dict( a=1, b=1, c=1, # グループ1 x=2, y=2, z=2, # グループ2 ) # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # 複数行にしたプロットの名称 legendgroup=legendgroup[name], # 凡例のグループ ) plot.append(d) # プロットを格納 # traceordersは+で結合可能 # 'normal+grouped'は'normal'なのでエラー traceorders = ('normal', 'reversed', 'grouped', 'reversed+grouped',) # font切り替え用のボタン作成 buttons = [] for num, traceorder in enumerate(traceorders): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=traceorder, # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=traceorder), legend=dict(traceorder=traceorder) ), ] ) buttons.append(button) # レイアウトにボタンを設置するためのupdatemenus作成 updatemenus = [ dict( active=1, # 初期グラフで見た目上、押されるボタン type='buttons', # ボタンのタイプはボタンに direction='right', # ボタンは右向きに配置 x=0.5, y=1.01, # ボタンの位置 xanchor='center', yanchor='bottom', # ボタンの位置の基準 buttons=buttons, # ここに設定したボタン情報を入れる ) ] # レイアウトの作成 layout = go.Layout( title='Default traceorder: grouped', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_traceorder" 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")
tracegroupgap
で凡例の間隔を変更
引数tracegroupgap
を使うことで、凡例グループ間の間隔を変更することが可能。デフォルトではtracegroupgap=10
でちょっと隙間がある。
ここではスライダーで隙間を変更した。より大げさにしたいなら数値を大きくすると良い。
import plotly.graph_objects as go import plotly.io as pio # 凡例のグループ同士の間隔を変更 # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # traceorder='grouped'のためにプロットをグループ化 legendgroup = dict( a=1, b=1, c=1, # グループ1 x=2, y=2, z=2, # グループ2 ) # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # 複数行にしたプロットの名称 legendgroup=legendgroup[name], # 凡例のグループ ) plot.append(d) # プロットを格納 # 凡例の間隔 tracegroupgaps = (0, 5, 10, 30) # スライダーの作成 steps = [] for tracegroupgap in tracegroupgaps: step = dict( method='relayout', label=tracegroupgap, args=[ # 各スライダーのグラフタイトルとフォントサイズ dict( title=dict(text=f"tracegroupgap = {tracegroupgap}",), legend=dict(tracegroupgap=tracegroupgap) ) ], ) steps.append(step) # レイアウトにスライダーを設置するためのsliders作成 sliders = [ dict( active=2, # 初期状態で表示するプロットのインデックス # 各スライダーの傾きをスライダーの上に表示 currentvalue=dict(prefix='tracegroupgap = '), steps=steps, # スライダー情報 ) ] # レイアウトの作成 layout = go.Layout( title='Default tracegroupgap = 10', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, sliders=sliders # スライダー設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_tracegroupgap" 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")
groupclick
で凡例グループクリック時の挙動を変更
引数groupclick
を使用することで、凡例グループをクリックしたときの挙動を変更することが可能。
デフォルトでは同じグループの凡例全てを表示したり非表示にしたりできるが、groupclick~’toggleitem’
にすると各プロットに対して表示・非表示を変更できる。
import plotly.graph_objects as go import plotly.io as pio # 凡例グループをクリックしたときの挙動 # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # traceorder='grouped'のためにプロットをグループ化 legendgroup = dict( a=1, b=1, c=1, # グループ1 x=2, y=2, z=2, # グループ2 ) # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # 複数行にしたプロットの名称 legendgroup=legendgroup[name], # 凡例のグループ ) plot.append(d) # プロットを格納 # 凡例グループをクリックしたときに各プロットが反応するか、グループで反応するか groupclicks = ('toggleitem', 'togglegroup') # font切り替え用のボタン作成 buttons = [] for num, groupclick in enumerate(groupclicks): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=groupclick, # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=groupclick), legend=dict(groupclick=groupclick) ), ] ) buttons.append(button) # レイアウトにボタンを設置するためのupdatemenus作成 updatemenus = [ dict( active=1, # 初期グラフで見た目上、押されるボタン type='buttons', # ボタンのタイプはボタンに direction='right', # ボタンは右向きに配置 x=0.5, y=1.01, # ボタンの位置 xanchor='center', yanchor='bottom', # ボタンの位置の基準 buttons=buttons, # ここに設定したボタン情報を入れる ) ] # レイアウトの作成 layout = go.Layout( title='Default groupclick: togglegroup', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_groupclick" 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")
itemclick
で凡例のクリック回数の挙動を変更
凡例をクリックするとプロットを表示・非表示にできるのがplotly
の大きなメリット。クリック回数に応じた挙動は引数itemclickm
, itemdoubleclick
で変更可能。
itemdoubleclick
は1回クリックで、itemdoubleclick
は2回クリック。それぞれtoggle
: クリックしたプロットを非表示、toggleothers
: クリックしたプロット以外を非表示を選択可能。
デフォルトではitemclicks=’toggle’,
itemdoubleclick=’toggleothers’
なのでシングルクリックでそのプロット、ダブルクリックでそのプロット以外が非表示になる。
import plotly.graph_objects as go import plotly.io as pio # 凡例をシングルクリック、ダブルクリックしたときの挙動を変更 # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # プロットの名称 ) plot.append(d) # プロットを格納 # 凡例をシングル・ダブルクリックした時の挙動 itemclicks = ('toggle', 'toggleothers') itemdoubleclicks = ('toggleothers', 'toggle') # font切り替え用のボタン作成 buttons = [] for num, (click, doubleclick) in enumerate(zip(itemclicks, itemdoubleclicks)): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=f"{click} x {doubleclick}", # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=f"1: {click}, 2: {doubleclick}"), legend=dict(itemclick=click, itemdoubleclick=doubleclick) ), ] ) 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='Default itemclicks: toggle itemdoubleclicks: toggleothers', # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_itemclick" 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")
itemsizing
で凡例のシンボルサイズを変更
通常、凡例に描かれるプロットのシンボルのサイズは実際にプロットされているシンボルのサイズに対応して大きくなったり小さくなったりする。
しかし、これだとプロットのシンボルが小さいものだと凡例でどんな形状(円とか三角とか)なのかがわかりにくい。
そこで、legend
の引数itemsizing
でデフォルトのプロットのサイズに合わせるのか一定のサイズに統一するのかを選択できる。
itemsizing=’trace’
で実際のシンボルサイズに対応、itemsizing='constant'
にすると一定のサイズとなる。
import plotly.graph_objects as go import plotly.io as pio # 凡例のシンボルサイズを変更 # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # 各プロットのマーカー(シンボル)2サイズ marker_size = dict( a=1, b=4, c=7, x=10, y=13, z=16, ) # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # プロットの名称 marker_size=marker_size[name], # プロットのマーカー(シンボル)サイズ ) plot.append(d) # プロットを格納 # プロットのマーカーサイズに合わせるか一定の大きさにするか itemsizings = ('trace', 'constant') # font切り替え用のボタン作成 buttons = [] for num, itemsizing in enumerate(itemsizings): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=itemsizing, # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=f"itemsizing: {itemsizing}"), legend=dict(itemsizing=itemsizing) ), ] ) 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='Default itemsizing: trace', # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_itemsizing" 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")
itemwidth
で凡例のシンボルの幅を変更
legend
の引数itemwidth
で凡例のシンボル表示幅を変更することができる。ここではプロットの線の長さが変わることで凡例の幅が変更されている。
凡例部分の長さが変わるので、プロット領域(グラフの水色の部分)外に凡例があるとプロット領域が狭くなることに注意。
import plotly.graph_objects as go import plotly.io as pio # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } # traceorder='grouped'のためにプロットをグループ化 legendgroup = dict( a=1, b=1, c=1, # グループ1 x=2, y=2, z=2, # グループ2 ) # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # 複数行にしたプロットの名称 legendgroup=legendgroup[name], # 凡例のグループ ) plot.append(d) # プロットを格納 # 凡例のシンボルの長さ itemwidths = (30, 50, 100, 500) # スライダーの作成 steps = [] for itemwidth in itemwidths: step = dict( method='relayout', label=itemwidth, args=[ # 各スライダーのグラフタイトルとフォントサイズ dict( title=dict(text=f"itemwidth = {itemwidth}",), legend=dict(itemwidth=itemwidth) ) ], ) steps.append(step) # レイアウトにスライダーを設置するためのsliders作成 sliders = [ dict( active=0, # 初期状態で表示するプロットのインデックス # 各スライダーの傾きをスライダーの上に表示 currentvalue=dict(prefix='itemwidth = '), steps=steps, # スライダー情報 ) ] # レイアウトの作成 layout = go.Layout( title='Default itemwidth = 30', # 初期グラフのタイトル # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, sliders=sliders # スライダー設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_itemwidth" 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")
orientation
で凡例の向きを変更
今更な感じがするが、引数orientation
で凡例の向きを変更することが可能。デフォルトはv
(vertical)で右上から右下にかけて凡例が並ぶ。
一方でorientation=’h’
とするとhorizontalの意味となり、凡例は左下から右下にかけて伸びることになる。さらに幅が足りなくなると自動で改行される。
ここではデータ数を50に増やしてスクロールバーの表示や改行を再現した。
import numpy as np import plotly.graph_objects as go import plotly.io as pio # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = {} for num in range(50): ys[f"data{num}"] = np.array([0, 2, 1]) + num # プロットを作成 plot = [] for name, y in ys.items(): d = go.Scatter( x=x, # 横軸の配列 y=y, # 縦軸の配列 name=name, # プロットの名称 ) plot.append(d) # プロットを格納 # プロットのマーカーサイズに合わせるか一定の大きさにするか orientations = ('v', 'h') # font切り替え用のボタン作成 buttons = [] for num, orientation in enumerate(orientations): # 一旦、プロット数だけ非表示にする visible = [False] * len(plot) # その後、各フォントに該当する位置のプロットを表示に変更 visible[num * len(plot):(num + 1) * len(plot)] = [True] * len(plot) # ボタンの作成 button = dict( label=orientation, # ボタンのラベル method='relayout', # ボタンの適用範囲はレイアウトのみ args=[ # 各ボタンのグラフタイトルと凡例にフォントを適用 dict( title=dict(text=f"orientation: {orientation}"), legend=dict(orientation=orientation) ), ] ) 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='Default itemsizing: trace', # グラフ全体とホバーのフォントサイズ変更 font_size=20, hoverlabel_font_size=20, updatemenus=updatemenus # ボタン設定 ) # グラフの作成 fig = go.Figure(data=plot, layout=layout) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_orientation" 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")
px
で凡例を編集
ここまでgo
を使ってlegend
の引数について解説したが、サクッとキレイなグラフを描けるpx
でも凡例の編集は可能。
ただし、px
の場合は一発でfig
を作成する(go
でもできるが本記事ではdata
とlayout
で分けて作成後にfig
を作成)ので、layout
を作成するにはfig
作成後に編集という形になる。
fig
作成後の編集はfig.update_layout
になるが、詳細については以下の記事参照。
-
-
【plotly&fig作成と更新】add_traceやupdate_layoutの使い方
続きを見る
また、px
はpandas
のデータフレームを使用すること多いので、以下でデータをpandas
のデータフレームに変換している。px
で散布図を作成する方法は以下の記事参照。
なお、ここではpx.scatter
で点のグラフ作成後に線を追加しているが、 px.line
で線のグラフを作成し、fig.update_traces
でmarker
を追加する方法もある。
-
-
【plotly&px.scatter】pxでの散布図の描き方
続きを見る
import numpy as np import pandas as pd import plotly.express as px import plotly.io as pio # 横軸の配列と縦軸の配列たち x = (0, 1, 2) ys = { 'a': (0, 1, 2), 'b': (1, 2, 0), 'c': (2, 1, 0), 'x': (10, 12, 11), 'y': (11, 10, 12), 'z': (12, 11, 10) } arr_x = x * len(ys) print(arr_x) # (0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2, 0, 1, 2) arr_y = np.array(list(ys.values())).flatten() print(arr_y) # [ 0 1 2 1 2 0 2 1 0 10 12 11 11 10 12 12 11 10] arr_name = sorted(list(ys) * len(x)) print(arr_name) # ['a', 'a', 'a', 'b', 'b', 'b', 'c', 'c', 'c', 'x', 'x', 'x', 'y', 'y', 'y', 'z', 'z', 'z'] df = pd.DataFrame(dict(x=arr_x, y=arr_y, name=arr_name)) print(df) # x y name # 0 0 0 a # 1 1 1 a # 2 2 2 a # 3 0 1 b # 4 1 2 b # 5 2 0 b # 6 0 2 c # 7 1 1 c # 8 2 0 c # 9 0 10 x # 10 1 12 x # 11 2 11 x # 12 0 11 y # 13 1 10 y # 14 2 12 y # 15 0 12 z # 16 1 11 z # 17 2 10 z fig = px.scatter(df, x='x', y='y', color='name') # プロット線+マーカーに fig.update_traces(mode='lines+markers') # # px.lineを使用する場合はマーカーを追加する # fig = px.line(df, x='x', y='y', color='name', markers=True) # グラフ全体とホバーのフォントサイズ変更 fig.update_layout(font_size=20, hoverlabel_font_size=20) fig.show() # グラフ保存 prefix = 'plotly-legend-summary' # 保存ファイル名の接頭辞 save_name = f"{prefix}_px" 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
の凡例legend
の引数まとめを行なった。一応、他にも引数はあるけど使用頻度が少ないと判断したので省いた。
凡例はプロットよりも地味だしないがしろにされるかもしれないが、凡例がないとどのプロットがどのデータかがわからん。当たり前。
Excelとかならそこまでカスタムできないけど、plotly
だと割とカスタムできるのでより見やすいグラフとなるように色々と工夫してほしい。
関連記事
-
-
【plotly&size, width】Scatterのサイズやlineの太さ一覧表を作成
続きを見る
-
-
【plotly&pattern】棒グラフとかのパターンまとめ
続きを見る
-
-
【plotly&pattern】棒グラフとかのパターンまとめ
続きを見る
-
-
【plotly&fill】px.areaで積み上げ塗りつぶし
続きを見る
-
-
【plotly&subplot】goでアニメーションのサブプロット
続きを見る
-
-
【plotly&subplot】goでアニメーションのサブプロット
続きを見る
-
-
【px&facet】plotly.expressでsubplotsを描く
続きを見る
-
-
【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加
続きを見る
-
-
【plotly&fill】goで領域を塗りつぶし
続きを見る
-
-
【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く
続きを見る
-
-
【plotly&グラフ内グラフ】plotlyでグラフ内に別グラフやグラフの一部拡大を描画
続きを見る