カテゴリー

plt

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

2021年8月7日

こんな人にオススメ

matplotlib.pyplotpltでグラフを保存。それはいいんだけど、グラフを保存しちゃうと静止画だから後で細かい部分を確認できないじゃん。

グラフを保存した後でも動かせる方法ってない?

っということで、今回は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

運営者のメガネです。YouTubeTwitterInstagramも運営してます。

自己紹介はこちらから、お問い合わせはこちら。

運営者メガネ

下準備

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を使用したグラフも作成するのでplotlyimportplotly_layout_templateは自作のplotlyテンプレート。以下参照。

【随時更新 備忘録】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でもグラフを動かすことができる。グラフを作成した後だ。上の画像の左下のようなマークでグラフを動かしたり拡大したり元に戻したりできる。

しかし、これはグラフを作成したその時だけに効くもので、閉じると終わり。だから、動かしたいならまたコードを回してグラフを作成しないといけない。面倒。

mpld3pltを動かせるグラフにする

さっきと同じグラフに見えるかもしれないが、一応今回の主役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&拡張機能】python・C・LaTeX...ユーザーのVisual Studio Code拡張機能

こんな人にオススメ Visual Studio Codeと& ...

続きを見る

一応、回避策として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でグラフを静止画として保存する際に出るポップアップ
【plotly&orca】plotlyで静止画保存(orca)

こんな人にオススメplotlyで画ࠔ ...

続きを見る


【plotly&px.scatter】pxでの散布図の描き方

こんな人にオススメ plotlyの中{ ...

続きを見る


【plotly&size, width】Scatterのサイズやlineの太さ一覧表を作成

こんな人にオススメ plotlyのScatterӗ ...

続きを見る


【python&csv読み込み】pythonを使ってcsvを読み込み

こんな人にオススメ pythonを使{ ...

続きを見る


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

こんな人にオススメ plotlyのlayoutの ...

続きを見る


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

こんな人にオススメ plotlyのlayoutの ...

続きを見る


【plotly&fig作成と更新】add_traceやupdate_layoutの使い方

こんな人にオススメ plotlyでグ} ...

続きを見る


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

こんな人にオススメ plotlyで塗| ...

続きを見る


(9 < x y2)の条件に合った塗りつぶし
【plt&fill_between】matplotlibで領域を塗りつぶし

こんな人にオススメmatplotlib.pyplot(plt ...

続きを見る

スイッチボット

2022/11/28

【SwitchBotロックレビュー】これからのスタンダードになりうるスマートロック

こんな人にオススメ SwitchBotからスマートロック「SwitchBotロック」が発売された ...

生活に役立つ

2022/11/28

【メガネ厳選】クソ便利に使っているサービスやアイテム達

このページでは執筆者「メガネ」が実際に使って便利だと感じているサ ...

マウス

2022/9/11

【Logicool MX ERGO vs MX Master 3】ERGOをメインにした決定的な理由

こんな疑問・お悩みを持っている人におすすめ 執筆者はLogicoolのハイエンӠ ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【ながら聴きイヤホン比較】SONY LinkBuds、ambie、BoCoはどれがおすすめ?

こんな人におすすめ 耳を塞がない開放型のイヤホンに完全ワイヤレスӟ ...

macOSアプリケーション

2022/10/15

【M1 Mac】MacBook Proに入れている便利でニッチなアプリを21個紹介する

こんな人におすすめ MacBookを購入してLINEとか必要最低限のアプリは入れた。 ...

完全ワイヤレスイヤホン(TWS)

2022/10/23

【SENNHEISER MOMENTUM True Wireless 3レビュー】高レベルでバランス型の高音質イヤホン

こんな人におすすめ SENNHEISER MOMENTUM True Wireless 3って実際のところどうなの? 評判は良い ...

完全ワイヤレスイヤホン(TWS)

2022/11/21

【SONY WF-1000XM4レビュー】神とゴミのハーフ&ハーフ

こんな人におすすめ SONYのフラグシップモデル「SONY WF-1000XM4」ってどれくらい性 ...

完全ワイヤレスイヤホン(TWS)

2022/8/19

【Nothing ear (1)レビュー】ライトな完成度、アップデートに期待

こんな人にオススメ 完全ワイヤレスイヤホン(TWS)でスケルトンボディ ...

Pythonを学びたいけど独学できる時間なんてない人へのすゝめ

執筆者は大学の研究室・大学院にて独学でPythonを習得した。

でも社会人になったら独学で行うには時間も体力もなくて大変だ。

時間がない社会人だからこそプロの教えを乞うのが効率的。

ここでは色んなタイプに合ったプログラミングスクールの紹介をする。

  • この記事を書いた人

メガネ

ベンチャー企業のWebエンジニア駆け出し。独学のPythonで天文学系の大学院を修了→新卒を1.5年で辞める→転職→今に至る。
常時金欠のガジェット好きでM1 MacBook Pro x Galaxy S22 Ultraの狂人。
人見知りで根暗だったけど、人生楽しもうと思って良い方向に狂う→人生が楽しい

ガジェットのレビューとPythonコードを記事にしています。ぜひ楽しんでください🦊
自己紹介と半生→変わって楽しいの繰り返し

-plt
-, ,