こんな人にオススメ
plotly
を使用して1つのグラフに表とプロットを表示したい。どうしたらいい?
あと、ボタンも追加してボタンに対応するプロットと表を強調したいんだけど、そんなことできる?
ということで、今回はploty
を使用して表とグラフを同時に描く方法を解説する。イメージは画像上に表を、下にグラフを描く感じ。こうすることで、データを見ながらグラフを確認できる。
今まではExcelとかで確認した生データとブラウザで確認したplotly
のグラフを一括で閲覧できるようになるとかなり便利になると思う。ぜひ習得指定いただきたい。
python環境は以下。
- Python 3.9.6
- matplotlib 3.4.2
- pandas 1.3.1
- plotly 5.1.0
- plotly-orca 3.4.2
作成したコード全文
下準備
import sys import matplotlib import pandas as pd import plotly import plotly.graph_objects as go import plotly.io as pio from plotly.subplots import make_subplots sys.path.append('../../') import plotly_layout_template as template
まずは下準備としてのimport
関連。plotly_layout_template
は自作のplotly
テンプレート。これを使用することで簡単にキレイなグラフを作成可能。
使用するデータ
df = pd.read_csv('./data.csv', index_col=0) print(df) # red blue green orange violet # Number # 1 10 8 8 0 3 # 2 6 5 3 9 3 # 3 4 6 5 3 4 # 4 10 2 6 2 7 # 5 3 1 5 9 2 # 6 5 1 2 8 0 # 7 9 0 6 3 3 # 8 0 7 0 5 1 # 9 0 10 1 0 9 # 10 3 8 2 5 1 # 11 6 8 7 4 7 # 12 10 5 7 0 1 # 13 0 7 2 0 6 # 14 6 3 4 1 4 # 15 6 6 10 4 10 # 16 2 10 1 0 7 # 17 9 1 4 6 10 # 18 5 0 4 6 0
今回は表を使用するということで、表のデータを作成した。値は乱数で生成した。まあ何だっていい。インデックスはデータ数を、ヘッダはデータ名を表している。
後々に楽をするためにヘッダ名は色の名称にしている。
サブプロットの定義用の関数
# subplot作成用 def set_subplots(rows, cols, specs, shared_xaxes=False, shared_yaxes=False): fig = make_subplots( rows=rows, cols=cols, # 行数と列数 shared_xaxes=shared_xaxes, # 横軸の値を共有するか否か shared_yaxes=shared_yaxes, # 縦軸の値を共有するか否か vertical_spacing=0, # 表とグラフの間の間隔を0に specs=specs, # subplotするグラフの配置順やグラフの種類 row_heights=[0.7, 0.45] # 表とグラフの高さを設定 ) return fig
表とプロットを1つのグラフに描くということで サブプロットを使用する。plotly
のサブプロットに関しては以下の記事で解説しているので参照。
-
-
【plotly&make_subplots】pythonのplotlyで複数グラフを1つの画像に描く
続きを見る
引数は以下の通り。
rows
: サブプロットで作成するグラフの行数cols
: サブプロットで作成するグラフの列数specs
: サブプロットのどこにどんなグラフを配置するのかshared_xaxes
,shared_yaxes
: サブプロットで横軸・縦軸を共有するか否か
その他、定義しているvertical_spacing
とrow_heights
についてはそれぞれの値がちょうどいい感じに配置できたから設定している。返り値のfig
がplotly
のグラフとなる。
表作成用の関数
# 表作成用 def get_table(h_values, c_values, line_color='silver', h_fill_color='lightgray', c_fill_color='whitesmoke',): # フォントの色はヘッダの色に設定。Numberは黒に設定 font_color = ['black'] + list(df.columns.values) table = go.Table( header=dict( values=h_values, # ヘッダの値 align="left", # ヘッダの並び方を左寄せに line_color=line_color, # ヘッダの枠線の色 fill_color=h_fill_color, # ヘッダの塗りつぶしの色 font_color=font_color, # ヘッダのフォントの色 ), cells=dict( values=c_values, # 各セルの値 align="left", # 各セルの並び方を左寄せに height=30, # セルの高さ line_color=line_color, # セルの枠線の色 fill_color=c_fill_color, # セルの塗りつぶしの色 font_color=font_color, # セルのフォントの色 ), ) return table
plotly
で表を作成するにはgo.Table
を使用すればいい。go.Tableではヘッダであるheader引数と各値であるcells引数という2種類の引数を駆使して設定を行う。引数は以下。
h_values
: ヘッダの値c_values
: 各セルの値line_color
: 表の枠線の色h_fill_color
: ヘッダの塗りつぶし色c_fil_color
; セルの塗りつぶしの色
基本はh_values
に1次元配列を、c_values
に2次元配列を入れて値を埋める。なお、自作テンプレートでフォントサイズやフォント自体を変更したらセルに収まらなかったのでheight=30
で調節した。
グラフ保存用の関数
# グラフ保存用の関数 def save(fig, config, save_name): pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{save_name}.html", config=config,) pio.write_image(fig, f"{save_name}.png")
グラフ保存用の関数。htmlとpngの形式で出力する。htmlで出力することで保存後も編集やグリグリ動かすことが可能。configはグラフを拡大したり図形を追加したりするためのツールバー的なもの。
-
-
【plotly&config】グラフのツールバーを編集する
続きを見る
上に表、下にグラフのsubplots
まずはシンプルに上に表を、下にグラフを描いてみる。なお、表のフォントの色をそれぞれのヘッダの色にしておいた。ヘッダ名が色なのはこのためだと言っても過言ではない。伏線だ。
なお、データ数を示すNumber
は黒色で表現しておいた。
2行1列で作成
def table_plot(rows, cols, specs, plot_col, save_name,): # subplotの作成 fig = set_subplots( rows=rows, cols=cols, specs=specs, shared_xaxes=True, # 横軸の値は共有 ) # 表の作成 table = get_table( h_values=df.reset_index().columns, # ヘッダの値 c_values=df.reset_index().values.T, # 各セルの値 ) fig.append_trace(table, 1, 1) # 1行目、1列目に配置 # プロットの作成 for num, color in enumerate(df): d = go.Scatter(x=df.index, y=df[color], name=color, line_color=color,) fig.append_trace(d, 2, plot_col(num)) # グラフの列番号は関数で指定 # レイアウトの作成 fig.update_layout( template=template.plotly_layout(), xaxis_title='Number', legend=dict(y=0, xanchor='left', yanchor='bottom'), ) # グラフの表示と保存 config = template.plotly_config() fig.show(config=config) save(fig=fig, config=config, save_name=save_name)
グラフを作成する手順は以下。
- サブプロットの枠組みを作成
- 表を作成(
df.reset_index()
でインデックスを一時的に消した) - プロットを作成
- レイアウトを作成
- グラフの表示と保存
変更することが必要な項目は引数に入れておいた。plot
を作成時のサブプロットのどこに入れるかを決めるfig.append_trace
で使用する引数のplot_col
は、列をどうするかを決める関数とした。
2行1列の本章初めのグラフなら1でいいんだけど、次で示すような2行2列のグラフにしたい時は関数にして、引数で列数をある法則に則って決められる方が自由度が高い。面倒だけど。
実際に使用する際には以下のように、1列であることを示すための関数が必要となるのが面倒なところ。
# シンプルに1を出力する関数 def simple(num): return 1 # 2行1列のsubplotで、上に表を、下にグラフを表示 table_plot( rows=2, cols=1, # 2行1列で区分け specs=[ [{"type": "table"}], # 上の行に表 [{"type": "scatter"}] # 下の行にグラフ ], plot_col=simple, # ここで列の情報を入れる save_name='table_plot11' )
ここでは2行1列のグラフを作成するので、spec
も2行1列のlistとした。なお、1行目が表なのでtype
はtable
にし、2行目は散布図なのでtype
はscatter
にする必要がある。
plot_col
は常に1を出力するsimple
関数を使用。これで常に1列目にプロットデータが割り当てられることになる。これでできるグラフが本章初めのグラフ。
2行2列でグラフを分ける
一方で、2行2列のグラフにしようとするなら工夫が必要。上のグラフの場合はヘッダが奇数番目は1列目、偶数番目は2列目のプロット領域でプロットされるようにした。
そのためにeven_odd
関数を定義し、2の余りの数で奇数と偶数を判別した。
# 偶数は1、奇数は2を出力する関数 def even_odd(num): return num % 2 + 1 # 2行2列のsubplotで、上に表を、下に左右でグラフを表示 table_plot( rows=2, cols=2, # 2行2列で区分け specs=[ [{"type": "table", "colspan": 2}, None], # 上の行に表は2列分の表 [{"type": "scatter"}, {"type": "scatter"}] # 下の行には2列分でグラフ ], plot_col=even_odd, # 偶数と奇数で描画列を変える save_name='table_plot12', )
また、今回は1行目が2列分使用するということなので、spec
のtable
には新たにcolspan
を2
に指定。この表で占領される1行目2列目はNone
でなしにした。
ボタンで表とプロットを強調
最後に、ボタンを使用して表とプロットを連携するグラフを作成。上のグラフだと、各ボタンを押すとそのボタンに応じて色が薄くなったり戻ったりする。
こんなグラフを使用することで、データをより強調して見せることが可能となる。
ボタン作成用の関数
# ボタン作成 def get_button(label, color): button = dict( label=label, method='update', args=[ { 'header.font.color': [color], # ヘッダのフォントカラー 'cells.font.color': [color], # セルのフォントカラー 'line.color': color, # プロットのカラー }, {}, ], ) return button
今回はボタンを作成するということで、予めボタンを設定するための関数を作成。今回は表のヘッダ、セル、そしてプロット点の透明度を変更したいので、args
の部分にはこれらを記述。
header.font.color
がヘッダのフォントの色を、cells.font.color
がセルのフォントの色を、そしてline.color
がプロットの色を司る。ボタンについては以下参照。
-
-
【plotly&ボタン】plotlyのupdatemenusにbuttonsを追加
続きを見る
透明度を変更したいが、それ専用の引数が存在しないので、色を再度設定する際に同時に透明度も変更する。なお、表に関する色ではlistを被せないと全セルで黒文字になる。
2行1列でボタン付きの表とグラフ
# 2行1列のボタン付きグラフの作成 def table_plot_buttons(rows, cols, specs, plot_col, save_name,): fig = set_subplots( rows=rows, cols=cols, specs=specs, shared_xaxes=True, # 横軸の値は共有 ) table = get_table( h_values=df.reset_index().columns, # ヘッダの値 c_values=df.reset_index().values.T, # 各セルの値 ) fig.append_trace(table, 1, 1) # 1行目、1列目に配置 for num, color in enumerate(df): d = go.Scatter(x=df.index, y=df[color], name=color, line_color=color,) fig.append_trace(d, 2, plot_col(num)) # グラフの列番号は関数で指定 # ------------------------------------------------------------ # ボタンの内容を作成 font_color = ['black'] + list(df.columns.values) # ボタン情報を入れる buttons = [] # 全グラフ表示用ボタン button = get_button(label='all', color=font_color) buttons.append(button) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # 各プロット表示・非表示用のボタン for color in df: # グラフプロットの色のみ rgbas = {} for color2 in font_color: # グラフプロットの色に加えてNumberの黒も # 色のstrをrgbに変換 rgb = matplotlib.colors.ColorConverter.to_rgb(color2) rgb = plotly.colors.convert_to_RGB_255(rgb) # rgbを0-1から0-255へ # Numberかボタンに該当する色は透明度を1にする if color2 == 'black' or color2 == color: opacity = (1,) else: # それ以外のプロットは透明度を0.1にする opacity = (0.1,) rgba = rgb + opacity rgbas[color2] = f"rgba({rgba})" colors = list(rgbas.values()) button = get_button(label=color, color=colors) buttons.append(button) # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # 作成したボタンをグラフ上に配置 updatemenus = [ dict( type='buttons', direction="right", active=0, buttons=buttons, x=0.5, y=1.01, xanchor='center', yanchor='bottom', ) ] # ------------------------------------------------------------ fig.update_layout( template=template.plotly_layout(), xaxis_title='Number', legend=dict(y=0, xanchor='left', yanchor='bottom'), updatemenus=updatemenus, # ボタンをレイアウトに配置 ) config = template.plotly_config() fig.show(config=config) save(fig=fig, config=config, save_name=save_name)
ということで、最後にグラフを作成するためのコード。関数化できそうな部分もあるが、もはや直接記述した。初めの方は先ほどと同様、サブプロットと表とプロットを作成。
その後、ボタンを作成するコードに移るが、今回は全体表示用と各色用の2種類のボタンを用意。それぞれに対してボタンを作成した。
各色用のボタンでは、black
などの色のstrをrgbに変換し、plt
とplotly
の間の値の変換(0-1から0-255へ)、そしてボタンに該当しないプロットの透明度を0.1にするという操作を実行。
最終的にrgba(255, 0, 0, 0.1)
のようにrgba
で記述してボタンの設定とした。結構面倒な作業だったが、やってることは色の値の変換と該当ボタン以外の透明度を0.1にしたということ。
# 2行1列のsubplotで、上に表を、下にグラフを表示 table_plot_buttons( rows=2, cols=1, # 2行1列で区分け specs=[ [{"type": "table"}], # 上の行に表 [{"type": "scatter"}] # 下の行にグラフ ], plot_col=simple, # ここで列の情報を入れる save_name='table_plot_buttons' )
上のコードのようにあとはプロットするだけで、本章初めのグラフを作成することができる。結構シンプル。
データを見ながらプロットを見る大切さ
今回はplotly
のsubplots
を使用して、上に表、下にプロットという複合グラフを作成する方法について解説した。実際にデータを見ながらプロットを確認できるのは結構楽ちん。
今まではアプリ間を行ったり来たりしたり、目線を移動させながらだったのが1グラフで済むから疲れにくいと考えられる。今回のグラフもプロットの非表示など応用が効くからいろいろ試してほしい。
関連記事
-
-
【plotly&ボタン】plotlyのupdatemenusに2回押し対応のbuttonsを追加
続きを見る
-
-
【plotly&スライダー】plotlyのslidersにスライダーを追加
続きを見る
-
-
【plotly&バブルチャート】plotlyで各国の収入と平均寿命の時代変化をバブルチャートで描く
続きを見る
-
-
【plotly&heatmap】go.Heatmapで2次元配列をマップ化
続きを見る