こんな人にオススメ
plotly
で指定できる項目が多いから毎回毎回コードをコピペしたり新規で書いたりするのが面倒だよ!何かテンプレートを用意して一括で管理できない?
pythonを使用してグラフを作成するには多くの方法、ライブラリが存在する。そしてその多くが
matplotlib.pyolot
、通称plt
を使用している。本記事ではplt
とは異なったライブラリであるplotly
でグラフを作成する際に実際に使用しているグラフ作成雛形を紹介する。python環境は以下。
- Python 3.9.2
- plotly 4.14.3
- numpy 1.20.1
pythonでグラフを作成する
pltを使う
多くの人が使うplt
での雛形と使用前後のグラフの様子については以下の記事参照。
-
-
【pltテンプレート】matplotlib.pyplotのグラフ作成テンプレート
続きを見る
plt
はすぐに静的なグラフを作成することに長けていると思う。パッと作ってパッと確認する、このような作業には向いているように感じる。
plotlyを使う
一方でこれから紹介するplotly
は作り込むことで簡単に動的に色んなことができるグラフを作成することができる。執筆者は新型コロナウイルス感染症のおうち時間で研究に使うグラフをもっと便利にしたいと思って始めた。大体1ヶ月くらいちょこちょこ調べたら構造がわかって簡単な動的グラフも作成することができた。plotly
で作成することができる動的な動作は例えば以下。
- 各プロット点の座標を表示
- 見たいプロットだけ残して後のプロットを非表示にする
- ボタンを押すとプロットや軸が変わる
- スライダーを使用してプロットの時間経過を表示
- htmlで保存することで後から動的なグラフを再現する
今までsubplotでグラフを並べたり複数枚のグラフを保存していたのをhtmlファイル1枚で完結することができ、さらに後から確認・編集することができるのはなかなか便利だと思う。
plotlyの構造
plotly
を使用したことがない方が多いと思うので、ここでは軽く構造を説明する。執筆者本人もまだまだ勉強中なので、不足していることや間違っていることがあるかもしれないのでそこはご了承ください。
大まかな流れ
plotly
でのグラフ作成には大きく分けて2種類の方法があると思う。
- 予めプロットとレイアウトの配列を作成し、その配列に必要な項目を随時アップデートする
- 予め必要な項目を配列に入れ、それをプロットとレイアウトの配列に入れる
すなわち最初に箱を作ってそこに中身をどんどん入れるか、最後に箱を作るのと同時に中身を入れるのかの違い。自分は後者の方に慣れているので後者の書き方に沿う。
雛形として設定している項目
レイアウト本体
執筆者の設定している項目は前節で言えばレイアウトの部分。プロットに関してはその時々にあった書き方をしたいのでデフォルトを設定していない。
設定しているレイアウトは以下。辞書型で書く必要があるので慣れていない人は慣れよう。このdef
のreturn
をレイアウト設定時に置くことでレイアウトを一式変更することができる。
def plotly_layout(): """plotlyのlayoutのtemplate Returns ------- dict plotlyのlayoutのtemplate 各グラフのlayout作成時に上書きすることも可能 """ layout_template = dict( layout=go.Layout( width=1280 * 1.5, height=720 * 1.5, title=dict( x=0., y=0.99, xanchor='left', yanchor='top', xref='paper', yref='container', ), xaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis2=dict( gridcolor='#d5d5d5', ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror=False, overlaying='y', side='right', ), font=dict( family='Times New Roman', color='black', size=15, ), paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', legend=dict( x=1, y=1, orientation='v', xanchor='right', yanchor='top', itemclick='toggleothers', itemdoubleclick='toggle', bgcolor='rgba(0,0,0,0)', bordercolor='black', borderwidth=1, ), showlegend=True, margin=dict(l=50), hoverdistance=30, showlegend=True, hovermode='closest', ) ) return layout_template
すごい量になるが一つ一つ解説していく。デフォルト値や引数の説明などはplotlyのサイトに載っている。
なお、レイアウトの中でも別途指定が必要な項目もある。これについては他の記事で書く予定。
グラフの幅と高さ
グラフの幅の高さは以下で指定する。
width=1280 * 1.3, height=720 * 1.3,
width
がグラフ幅でheight
がグラフ高さを示す。ここで気をつけないといけないのが、このグラフ幅はプロット領域も軸ラベルとかのある枠も含めたグラフ全体のサイズということ。後述するプロット用のコードでプロットの凡例の名前が長すぎるとどんどんプロット部分が縦長になってしまう。
2021年4月14日更新
width
とheight
を設定すると、記事中に表示したグラフが自動サイズ変更(レスポンシブ対応)しなくなるので、当面の間width
とheight
については設定しません。
グラフタイトル
グラフタイトルは以下で指定する。辞書型の中の引数で内容を指定している。
title=dict( x=0., y=0.99, xanchor='left', yanchor='top', xref='paper', yref='container', ),
x
, y
でタイトルの位置を相対的に指定する。xanchor
, yanchor
はx
, y
を計測する基準点をどこにするか。xref
, yref
はx
, y
の置く位置の基準点を決める。'paper'
の場合はプロット領域、'container'
の場合はグラフ全体。自分は横位置はプロット領域の左端と同じように、縦位置はグラフ全体の上端から0.01だけ下の部分がタイトルボックスの上端に一致するように設定している。
なぜ横位置を左にしているかというと、初めてグラフを作成したときに左に寄っていたから。plt
と違って違和感あるなと思いつつ、それがplt
とplotly
のパッと見の違いを出していたからそのまま左にしている。
横軸、縦軸、第二縦軸
横軸はxaxis
、縦軸はyaxis
、第二縦軸はyaxis2
で以下のように指定している。
xaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis2=dict( gridcolor='#d5d5d5', ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror=False, overlaying='y', side='right', ),
それぞれの引数について以下に示す。
まずは第一軸に関する引数
ticks='inside'
:目盛線の向きを内側にするzeroline=False
:それぞれ該当する軸の0の値に直線を引かないlinewidth=1
:軸の太さを1に設定showline=True
:軸の線自体を書くlinecolor='black'
:軸の色を黒にするmirror='allticks'
:反対側の軸にも目盛を振る
次に第二縦軸に関する引数
gridcolor='#d5d5d5'
:グリッドの色を#d5d5d5
に変更する(グリッド自体はデフォルトでON)mirror=False
:反対側の軸・目盛をOFFにする。しないと軸や目盛が被ってしまうoverlaying='y'
:第一軸のy
に対して重ねるside='right'
:第二縦軸は右側に配置
現状のままだと二軸グラフにした時にグリッドが重なるのを色で防いでいるが、もう二軸グラフの時はグリッドはないほうがいいかも。
フォント
フォントについては以下のように指定している。
font=dict( family='Times New Roman', color='black', size=15 ),
フォントは以前のplt
の記事でも言ったようにTimes New Roman
を使用し、フォントサイズは大きめにしている。フォントについてのお話は以下の記事参照。
-
-
【pltテンプレート】matplotlib.pyplotのグラフ作成テンプレート
続きを見る
グラフ背景
グラフ背景に関しては以下のように設定している。グラフの背景は無色透明。スライドに載せるときなどにグラフ重ねが楽。
paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)',
plt
と異なるのが以下の点。
- RGBAの時は
(0, 0, 0, 0)
の指定ではなくrgba(0, 0, 0, 0)
のように最初にrgba
とつける必要がある plt
の時はRGBは0から1の間で値を決めていたが、plotly
の場合は0から255になる
特に後者の色に関しては変換しないといけないので面倒。勝手に変換してくれる関数があったような気がしたがパッと出ないので出た時にでもまた記事にする。
また、paper_bgcolor
だけ指定するとグラフ全体が、plot_bgcolor
を指定するとpaper_bgcolor
を指定していてもプロット部分の色はplot_bgcolor
の色が反映される。
凡例
プロットの凡例に関しては以下のように設定している。凡例中での改行ができないので(プロット点のグループ化で擬似的にはできる)、とりあえずはグラフのレイアウトが崩れないように設定している。
legend=dict( x=1, y=1, orientation='v', xanchor='right', yanchor='top', itemclick='toggleothers', itemdoubleclick='toggle', bgcolor='rgba(0,0,0,0)', bordercolor='black', borderwidth=1, ), showlegend=True,
legend
の辞書のそれぞれの引数について新出のものだけ以下に示す。
orientation='v'
:次の凡例を下に続けるitemclick='toggleothers'
:凡例を一回クリックした時、クリックしたプロット以外のプロットを非表示itemdoubleclick='toggle'
:凡例をダブルクリックした時、クリックしたプロットを非表示bgcolor='rgba(0,0,0,0)'
:凡例のボックスの背景色を無色透明にする。すでに述べたように全部0なので無色透明bordercolor='black'
:凡例のボックスの枠の色を黒にするborderwidth=1
:凡例のボックスの枠の線の太さを1にする
showlegend
については、デフォルトで凡例が出ないようになっていたので明示的にTrue
にしている。
余白
margin=dict(l=50),
デフォルトは80でやたらと右寄りの印象があったので左の余白を減らした。減らしすぎると縦軸と縦軸ラベルが重なるので注意。
ホバーを判定する距離
plotly
にはプロット点付近にマウスカーソルを近づけるとその値が表示されるという機能が備わっている。このホバーを認識するための距離として以下のように設定している。
hoverdistance=30,
デフォルトが20で執筆者の指定が30。実は結構動作が変わっていて試しに300とかにするとどこにマウス持っていってもどこかしらのプロット点に反応するレベルになる。
凡例は常に表示
showlegend=True,
2021年5月7日更新。
プロットの凡例が表示されないことがあるので凡例を表示するように明示。
マウスオーバーは1プロットのみ
hovermode='closest',
2021年5月7日更新。
マウスオーバーした時に複数プロットの情報を見ることができるようにすると見にくいので、デフォルトでは1プロットの情報だけ表示。右上のツールバーで変更可能。
実際にグラフ化
ここでは実際に上記のテンプレートを使用する前後でグラフがどう変わるのかを示す。本記事で書くと長くなるので、今回はグラフをプロットする時の細かいコードや保存時の注意については触れない。また他の記事で書く予定。
今回使用するコードは以下。
import plotly import plotly.graph_objects as go import plotly.io as pio import numpy as np def plotly_layout(): """plotlyのlayoutのtemplate Returns ------- dict plotlyのlayoutのtemplate 各グラフのlayout作成時に上書きすることも可能 """ layout_template = dict( layout=go.Layout( # width=1280 * 1.5, height=720 * 1.5, title=dict( x=0., y=0.99, xanchor='left', yanchor='top', xref='paper', yref='container', ), xaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis=dict( ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror='allticks', ), yaxis2=dict( gridcolor='#d5d5d5', ticks='inside', zeroline=False, linewidth=1, showline=True, linecolor='black', mirror=False, overlaying='y', side='right', ), font=dict( family='Times New Roman', color='black', size=15, ), paper_bgcolor='rgba(0,0,0,0)', plot_bgcolor='rgba(0,0,0,0)', legend=dict( x=1, y=1, orientation='v', xanchor='right', yanchor='top', itemclick='toggleothers', itemdoubleclick='toggle', bgcolor='rgba(0,0,0,0)', bordercolor='black', borderwidth=1, ), showlegend=True, margin=dict(l=50), hoverdistance=30, ) ) return layout_template def graph(name, template=None): """2次関数をグラフ化 Parameters ---------- name : str 作成したグラフの保存名 template : dict or NoneType, optional レイアウトのテンプレートを指定, by default None """ x = np.arange(-10, 10 + 1) y = x ** 2 # プロットするデータを配列に入れる plot = [] d = go.Scatter( mode='lines+markers', x=x, y=y, name='plot1', marker=dict(symbol='circle'), line=dict(dash='solid', color='blue'), hoverlabel=dict( font=dict( family='Times New Roman', size=15 ) ), hovertemplate='ホバーだよ<br>' + 'x=%{x}<br>' + 'y=%{y}<br>' + "<extra>ホバー2</extra>", ) plot.append(d) # レイアウトを決める layout = go.Layout( template=template, title=dict( text='title', ), xaxis=dict( title='x', ), yaxis=dict( title='y', ), ) # 作成したデータの入った配列と、レイアウトの配列を図にする fig = go.Figure(data=plot, layout=layout) plotly.offline.iplot(fig) # 作成したグラフを保存 plotly.io.orca.config.executable = ('/Applications/orca.app/' 'Contents/MacOS/orca') pio.write_html( fig, f"plotly-base_plotly_{name}.html", auto_open=False ) pio.write_image(fig, f"plotly-base_plotly_{name}.png") # template=Noneだとテンプレートは反映されない graph(name='before', template=None) # template=plotly_layout()だとテンプレートが反映される graph(name='after', template=plotly_layout())
テンプレートを使用する前
テンプレートを使用する前、すなわちデフォルトの設定では以下のようなグラフになる。
デフォルトのplotlyでのグラフ
ploly
のグラフはデフォルトではプロット部分の色が#e6ecf5
(RGB:230, 236, 245)くらいの色になっている(MacのDigital color meterで測定)。また、フォントが異常に小さい印象。
以下に実際に作成したグラフのHTMLバージョンを示す。
テンプレートを使用した後
テンプレートを使用した後、すなわちレイアウトを変更した後の設定では以下のようなグラフになる。
テンプレート適用後のplotlyでのグラフ
背景は無色透明で目盛は内側で上下左右に配置。これでもまだフォントは小さいがそれでも少しは大きくなっている。また、マウスカーソルをプロット点に近づけるとそのプロットのデータが表示されるほか、凡例をダブルクリックすることでプロット点を非表示にすることもできる。
以下に実際に作成したグラフのHTMLバージョンを示す。グラフはレスポンシブ対応しているはずなので、PCで閲覧している方はウィンドウサイズを変更してみてください。グラフのサイズが変更されるはず。
plotlyはカスタマイズが簡単にできる
今回はplotly
のレイアウトのテンプレートを紹介した。今回もplt
と同様、カスタム方法は千差万別。凡例をクリックする時のラグだったり各種位置だったり色々と設定を試してみて楽しんでほしい。
これから当ブログで使用するplotly
のグラフのレイアウトは基本的には上述のテンプレートに沿って作成していく。個人的にこの設定に慣れたから外観違うのは許して。他記事でもっと拡張子したカスタムを公開するので公開までしばしお待ちを。