こんな人にオススメ
matplotlib.pyplot
、plt
でグラフを保存。それはいいんだけど、グラフを保存しちゃうと静止画だから後で細かい部分を確認できないじゃん。
グラフを保存した後でも動かせる方法ってない?
っということで、今回はplt
を使って保存した後でも動かせるグラフを作成する。どういうことかというと、pltでグラフを保存すると静止画だから動かせない。しかし、本記事の方法を使用すると、保存した後でもグラフを動かすことができる。
グラフを動かすってのはplotly
が得意な部分ではあるけど、plt
とはコードの書き方が結構違うからとっつきにくい。そんな人にはplt
のまま少しコードを咥えたりするだけでできるmpld3
がおすすめ。
python環境は以下。
- Python 3.9.6
- numpy 1.21.1
- matplotlib 3.4.2
- mpld3 0.5.5
- plotly 5.1.0
- plotly-orca 3.4.2
下準備
import sys import numpy as np import matplotlib.pyplot as plt import mpld3 import plotly.graph_objects as go import plotly.io as pio sys.path.append('../../') import plotly_layout_template as template x = np.linspace(0, 2 * np.pi) y1 = np.sin(x) y2 = np.cos(x)
まずはグラフ作成用のimport
関連とデータの作成。最後にplotly
を使用したグラフも作成するのでplotly
もimport
。plotly_layout_template
は自作のplotly
テンプレート。以下参照。
-
-
【随時更新 備忘録】plotlyのグラフ即席作成コード
続きを見る
通常のplt
だとグラフ保存後に動かせない
plt
。それはpythonでグラフを作成する人誰もが通るだろうライブラリ。簡単にグラフを作成できるしカスタムも豊富。さらにユーザー数も半端じゃない数いるので困った時も解決策がサイトにゴロゴロ。
しかし、plt
の個人的最大の弱点が、グラフを保存した後に動かせないということ。当たり前だけど静止画で保存すると画像として保存されるので動かせない。
動かせないから執筆者は動かすことに特化しているplotly
に電撃移籍したわけだが、本記事ではなんとplt
でも動かせるようにした。
グラフを眺めるだけなら保存でいい
def plt_sin_cos(): fig, ax = plt.subplots(1, 1) ax.set_title('title') ax.set_xlabel('x') ax.set_ylabel('y') ax.plot(x, y1, 'o-', ms=4, label='sin') ax.plot(x, y2, '^-', ms=4, label='cos') ax.legend() # 凡例を追加 ax.grid(which='major', ls='-') ax.grid(which='minor', ls='--', alpha=0.5) fig.savefig('plt_sin_cos.png') fig.show() plt_sin_cos()
本章の始めに描いたグラフは上のコードで作成したものだが、静止画としてpng
を使用しているので、グラフを保存した後は細かい部分の拡大やグラフの移動ができない。
ならplotly
みたいに.html
形式で保存すればいいじゃないかということで、試してみた。ダメです。
fig.savefig('plt_sin_cos.html') # ValueError: Format 'html' is not supported (supported formats: eps, jpeg, jpg, pdf, pgf, png, ps, raw, rgba, svg, svgz, tif, tiff)
要するに画像として保存しろってことだろう。拡張子について詳しくないけどこれを見る限りそうだと思う。
保存後も動かしたい
一応、plt
でもグラフを動かすことができる。グラフを作成した後だ。上の画像の左下のようなマークでグラフを動かしたり拡大したり元に戻したりできる。
しかし、これはグラフを作成したその時だけに効くもので、閉じると終わり。だから、動かしたいならまたコードを回してグラフを作成しないといけない。面倒。
mpld3
でplt
を動かせるグラフにする
さっきと同じグラフに見えるかもしれないが、一応今回の主役mpld3
で作成したグラフだ。コードは以下。
def sin_cos(): fig, ax = plt.subplots(1, 1) ax.set_title('title') ax.set_xlabel('x') ax.set_ylabel('y') ax.plot(x, y1, 'o-', ms=4, label='sin') ax.plot(x, y2, '^-', ms=4, label='cos') ax.legend() # 凡例を追加 ax.grid(which='major', ls='-') # 副目盛は表示されないっぽい ax.grid(which='minor', ls='--', alpha=0.5) name = 'sin_cos' fig.savefig(f"{name}.png") mpld3.save_html(fig, f"{name}.html") # HTML形式で保存 mpld3.show() # HTMl形式でブラウザで表示 sin_cos()
ほとんど通常のplt
と変わらない。変わる点としてはhtml
で出力して表示するという点。このコードを動かすと自動でブラウザが起動してそこにグラフが作成される。plotly
と同じ。
デメリットはグラフを描くたびに一回中止とかしないといけないということ。どういうことかというと、グラフを表示するとその処理で止まってしまい、後の処理に進まないということ。
これのダルいところがVScodeの拡張機能Code Runnerだと一回グラフを作成したら進めないという点。一生そのグラフを表示している処理で止まる。VScodeの拡張機能については以下。
一応、回避策としてVScodeの設定で「Code-runner: Run In Terminal」をオンにしたらctrl c
で処理を強制終了して進めるが、オフにしていると先に進めない。オフにしているとコンソールに色がついて見やすくなるから取捨選択。
多少使い勝手は悪いが動かせる
グラフを作成してブラウザで表示すると上の画像のような感じでグラフが表示される。で、左下に元の状態に戻すボタンと移動ボタン、拡大ボタンが出てくる。
どうやらplt
のグラフショートカットは使用できないようなので、拡大したい時はいちいちこのボタンを押さないといけない。面倒。
本当は動かせるグラフを埋め込みたかったけどやり方がわからなかったのでスルー。plotly
の時はやり方があったけど、それとはまた別なので個人で色々試してほしい。
ズーム時の挙動がヌルヌル
mpld3
で作成したグラフを拡大・縮小するとやたらとヌルッと動く。謎だけどヌルヌル。上のgifだとあまりスムーズになってる感はないけど、実際には結構ヌルヌル。
凡例とホバーを改造
とりあえずplt
のグラフをhtmlで作成できた。そして拡大とかできるようにした。しかし、これではなかなかに貧相。ということで、ここでは凡例とホバーを作成する。
凡例で上のgifで言えばグラフ右側にsinとcosがどの色かを示しているけど、これにカーソルを合わせると濃くなるし、クリックすればそのグラフが薄くなる。
また、プロット線やプロット点にカーソルを合わせるとその情報が表示される。マウスオーバーとかホバーとか言う。これらはplotly
でも作成可能。
考え方はプラグイン
def sin_cos_hover_legend(): fig, ax = plt.subplots(1, 1) ax.set_title('title') ax.set_xlabel('x') ax.set_ylabel('y') plot1 = ax.plot(x, y1, 'o-', ms=4, label='sin') plot2 = ax.plot(x, y2, '^-', ms=4, label='cos') ax.grid(which='major', ls='-') ax.grid(which='minor', ls='--', alpha=0.5) # 凡例のマウスオーバーを作成 handles, labels = ax.get_legend_handles_labels() # 凡例情報を取得 # 凡例のマウスオーバーを設定 interactive_legend = mpld3.plugins.InteractiveLegendPlugin( handles, labels, alpha_unsel=0.2, # 凡例をクリックして非表示にした時の透明度(1以上は線の太さ) alpha_over=2, # 凡例にマウスオーバーした時の透明度(1以上は線の太さ) ) mpld3.plugins.connect(fig, interactive_legend) # 作成したホバーをグラフに追加 # プロット点のマウスオーバーを作成 # PointLabelTooltip と似ているがこちらはHTML記法が使えないっぽい text1 = [f"x: {i:.2}<br>y: {j:.2}" for i, j in zip(x, y1)] # y1 tooltip1 = mpld3.plugins.PointHTMLTooltip( plot1[0], labels=text1, voffset=30, hoffset=20, # カーソルからホバー情報までの垂直・平行距離 ) text2 = [f"x: {i:.2}<br>y: {j:.2}" for i, j in zip(x, y2)] # y2 tooltip2 = mpld3.plugins.PointHTMLTooltip( plot2[0], labels=text2, voffset=30, hoffset=20, # カーソルからホバー情報までの垂直・平行距離 ) # プロット線上でマウスオーバーした時の出力 tooltip3 = mpld3.plugins.LineLabelTooltip(plot1[0], label='sin') tooltip4 = mpld3.plugins.LineLabelTooltip(plot2[0], label='cos') tooltip = [ tooltip1, tooltip2, tooltip3, tooltip4, ] # 作成したホバーをグラフに追加 mpld3.plugins.connect(fig, *tooltip) name = 'sin_cos_hover_legend' fig.savefig(f"{name}.png") # mpld3で画像の保存は簡単にはできないっぽい mpld3.save_html(fig, f"{name}.html") # HTML形式で保存 mpld3.show() # HTMl形式でブラウザで表示 sin_cos_hover_legend()
上のコードが凡例とホバーを改造した後のグラフのコードだけど長い。で、mpld3では基本的にプラグイン(plugins
)を使って機能を追加していく。
今回だと以下の3種類のプラグインで機能を追加。
- 凡例
- プロット点
- プロット線
プラグインについてはGitHubのこのページに書いてある。全てのプラグインがあるかはわからないけど、大体のことはできそう。
凡例をクリックで表示・非表示を切り替え
# 凡例のマウスオーバーを作成 handles, labels = ax.get_legend_handles_labels() # 凡例情報を取得 # 凡例のマウスオーバーを設定 interactive_legend = mpld3.plugins.InteractiveLegendPlugin( handles, labels, alpha_unsel=0.2, # 凡例をクリックして非表示にした時の透明度(1以上は線の太さ) alpha_over=2, # 凡例にマウスオーバーした時の透明度(1以上は線の太さ) ) mpld3.plugins.connect(fig, interactive_legend) # 作成したホバーをグラフに追加
凡例についてはこの部分だけど、まずはax.get_legend_handles_labels()
で凡例の情報を取得。そしてmpld3
の凡例用のプラグインInteractiveLegendPlugin
を使ってクリックした時とホバー時の状態を設定。
最後にmpld3.plugins.connect
でグラフにプラグインをくっつけると完成。毎回くっつけないといけないのは少し面倒。
プロット点をホバーした時に値を表示
# プロット点のマウスオーバーを作成 # PointLabelTooltip と似ているがこちらはHTML記法が使えないっぽい text1 = [f"x: {i:.2}<br>y: {j:.2}" for i, j in zip(x, y1)] # y1 tooltip1 = mpld3.plugins.PointHTMLTooltip( plot1[0], labels=text1, voffset=30, hoffset=20, # カーソルからホバー情報までの垂直・平行距離 ) text2 = [f"x: {i:.2}<br>y: {j:.2}" for i, j in zip(x, y2)] # y2 tooltip2 = mpld3.plugins.PointHTMLTooltip( plot2[0], labels=text2, voffset=30, hoffset=20, # カーソルからホバー情報までの垂直・平行距離 )
プロット点をホバーした時にその点の値を表示するには上のようなコードを作成する。PointHTMLTooltip
は代わりにLineLabelTooltip
でもいいが、PointHTMLTooltip
の方がHTMLのタグが使えるので汎用性は高い。
tooltip1
とかtooltip2
っていうのは後でまとめてグラフにコネクトするために一時的に変数としておいてある。毎回コネクトしてもいいけど、面倒なので一括コネクト。後で行う。
plot1[0]
とか[0]
とするのはよくわからんけど、色んなサイトでそうしているので合わせている。理由はどこかに書いていたけど忘れた。執筆者はmpld3
は使わないからとりあえず放置。
プロット線をホバーした時はグラフ名を表示
# プロット線上でマウスオーバーした時の出力 tooltip3 = mpld3.plugins.LineLabelTooltip(plot1[0], label='sin') tooltip4 = mpld3.plugins.LineLabelTooltip(plot2[0], label='cos') tooltip = [ tooltip1, tooltip2, tooltip3, tooltip4, ] # 作成したホバーをグラフに追加 mpld3.plugins.connect(fig, *tooltip)
プロット線上でもホバーできるようにするにはLineLabelTooltip
を使用する。基本的な使い方はPointHTMLTooltip
と同じだが、LineLabelTooltip
では1つのlabelを使用する。
プロット線のホバー設定が終われば、tooltip1
からtooltip4
を一括でtooltip
に入れて一括でコネクト。コネクト時には展開しているので、シンプルにtooltip1
, tooltip2
のように並べて書いてもいい。
plotly
でも描いてみる
最後に、凡例とホバーの設定をしたsin_cos_hover_legend
をplotlyでも書いてみた。プロット線のホバーについては適用していないが、基本的には同じようなグラフが作成可能。
慣れの問題もあるけど、執筆者的にはplotly
推し。他にもボタンを追加したりスライダーを追加したりするのが簡単なのも背中を押す1つの要因。mpld3
はまだまだ発展途上のようで、できることが少ないっぽい。
def plotly_sin_cos_hover_legend(): plot = [] for name, y in zip(('sin', 'cos'), (y1, y2)): d = go.Scatter( mode='lines+markers', x=x, y=y, name=name, hovertemplate=f"{name}<br>" + 'x: %{x} <br>' + 'y: %{y} <br>' + "<extra></extra>", ) plot.append(d) layout = go.Layout( template=template.plotly_layout(), title=dict(text='',), xaxis=dict(title='',), yaxis=dict(title='',), ) fig = go.Figure(data=plot, layout=layout) fig.show(config=template.plotly_config()) name = 'plotly_sin_cos_hover_legend' pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html( fig, f"{name}.html", config=template.plotly_config() ) pio.write_image(fig, f"{name}.png") plotly_sin_cos_hover_legend()
使い勝手はplotly
の方がダントツ良し
今回はplt
のグラフをmpld3
を使用してhtml出力する方法について解説した。こうすることで、グラフを作成した後でも拡大とかができるので結構便利。
しかし、使い勝手的にはダントツでplotly
が良いと思う。それに本家のplt
まではいかないにもかなり色んな人がグラフの作成例とかを出してくれているのでplotly
で良いような気がする。
まあ、知っていても損はないから知ってる程度にする。これからもplotly
を使い続ける。ちなみにmpld3
公式のグラフ例はここから見ることができる。
関連記事
続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る 続きを見る
【plotly&go.Scatter】plotlyの散布図グラフの描き方
【plotly&orca】plotlyで静止画保存(orca)
【plotly&px.scatter】pxでの散布図の描き方
【plotly&size, width】Scatterのサイズやlineの太さ一覧表を作成
【python&csv読み込み】pythonを使ってcsvを読み込み
【plotly&legendまとめ】凡例の引数一覧
【plotly&legendまとめ】凡例の引数一覧
【plotly&fig作成と更新】add_traceやupdate_layoutの使い方
【plotly&fill】goで領域を塗りつぶし
【plt&fill_between】matplotlibで領域を塗りつぶし