今回はPythonのPlotly
で大量のデータを軽く扱うことができるgo.Scattergl
の使い方とgo.Scatter
との比較を行う。1万以上のデータを散布図として扱う際はこの記事を参考にgo.Scattergl
を活用してほしい。
もちろんgo.Scatter
で1万のデータをグラフ化することも可能だが、グラフ作成時間やグラフ表示後の動きがカクカクして使いづらい。そんな時にWebGLを使用して動作するgo.Scattergl
が活躍する。
本記事を通して1万や10万といった大量のデータを簡単に扱えるようになってほしい。
Python環境は以下。
- Python 3.10.8
- numpy 1.24.0
- plotly 5.11.0
- plotly-orca 3.4.2
参考になるサイト
Plotly公式
Qiita
Plotly Community Forum
本記事のコード全文
下準備のimport
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
まずは下準備としてのimport
関連。今回はgo
を使用する。pio
はplotly
でのグラフ保存用のライブラリ。保存の仕方は色々あるがpio
はその1つだ。
また、本記事の最後にScatter
とScattergl
の描画速度比較をするのでtime
もimport
しておく。
Scatter
とScattergl
の違いと使い方
一般的に使われるgo.Scatter
はsvg(ベクトル方式)を採用しており、グラフ化したりエクスポートした際に見やすくキレイな仕上がりになるのが特徴だ。また、一部の雑誌やWebサイトはこのsvgのみを公開している。
一方でgo.Scattergl
はWebGL方式(ラスター方式)を採用しており、JavaScriptとGPUを使用して描画するので高速だ。GPUを使うことでCPUでは賄いきれない高処理負荷のグラフを描画可能。
ただしさらに一部のブラウザやPCではセキュリティの観点からWebGLが無効にされていたり学術論文では非推奨だ。
ということで私はgo.Scattergl
を使うタイミングとしては以下の状況を考えている。
go.Scatterglを使うタイミングの目安
- 10,000データを超えるグラフ
- グラフの動きがモッサリしてきた
- 見栄えより動作が重要
基本、大量のデータを扱わない限りは他のグラフと見た目上の差異がないgo.Scatter
を使用するのが良いだろう。
Scattergl
なら10万データをプロットしてもなめらか
ということで実際にgo.Scattergl
を使用して10万データの散布図を作成してみた。ただ、このサイトに載せる際にデータ数が多すぎると表示できなかったので、サイト上ではデータ数を10,000に絞った。
実際にデータを触ってみるとわかるように、Scattergl
を使うことで10,000データでも滑らかに動作させることが可能だ。
以下にScattergl
で100,000データを描画し操作したときのgifを載せておく。
かなり滑らかに動作していることがわかるだろう。これだけ動かしているがデータ数は10万だ。
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
# 10万データをScatterglでプロットする
num = 100000
np.random.seed(1)
x = np.random.randn(num)
y = np.random.randn(num)
plot = [
go.Scattergl(
x=x, y=y, mode='markers',
marker=dict(color=y, colorscale='jet'),
)
]
layout =go.Layout(title='Scattergl')
fig = go.Figure(data=plot, layout=layout)
# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()
# グラフ保存
prefix = 'go-scattergl' # 保存ファイル名の接頭辞
save_name = f"{prefix}_scattergl_{num}"
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")
Scatter
だと動作がかなりもっさりする
一方で、通常のgo.Scatter
を使用して10,000データの散布図を作成すると上のグラフのようになる。動かすことは可能だが、Scattergl
に比べると少し引っ掛かりがあるように感じる。
これくらいのデータ数ならそこまで問題なく動作するが、これが10万データになるとかなりガクガクになってしまう。
拡大はできるものの縮小時のダブルクリックが動作しない。右上の家マークから初期表示に戻すことは可能だが、この際にも動作に遅れがある。
やはり大きなデータを扱う際にはgo.Scatter
ではなくgo.Scattergl
を使うのが良いだろう。
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
# 10万データをScatterでプロットする
num = 100000
np.random.seed(1)
x = np.random.randn(num)
y = np.random.randn(num)
plot = [
go.Scatter(
x=x, y=y, mode='markers',
marker=dict(color=y, colorscale='jet'),
)
]
layout =go.Layout(title='Scatter')
fig = go.Figure(data=plot, layout=layout)
# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()
# グラフ保存
prefix = 'go-scattergl' # 保存ファイル名の接頭辞
save_name = f"{prefix}_scatter_{num}"
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")
Scattergl
の100万データで動作が遅くなる問題は解決
大データを散布図で扱うにはgo.Scattergl
が便利という話をしてきたが、どうやら100万以上のデータ数の場合は動作がエラーになったり極端に遅くなっていたらしい。
ただしissueにあるように解決したとのこと。この事例は類似のものだが、実際にグラフを描画しても正常に描画・保存・表示ができた。100万データもあるのでちょっと引っかかりはあるが動く。
mode=’markers+text’
でプロットの値を追加
基本的に大量のデータを扱うときはそのデータの多さから点プロットだけ表示することが多いが、プロットした値をプロット上に表示することも可能。
プロットの値を表示する詳しい方法は以下の[go.Scatter
の記事に](https://programming.megatenpa.com/plotly/go/go-scatter/)て解説している通り引数mode
で可能。今回は点プロットとテキストを表示したいのでmode=’markers+text’
にした。go.Scatter
では引数text
でtext=y
とすることでプロット点にその点のy
の値が表示される。
-
【Plotlyで散布図】go.Scatterのグラフの描き方まとめ
これからPloltyで動くグラフを作りたい、もっとグラフをキ ...
続きを見る
一方でgo.Scattergl
では単にtext=y
とするとプロット点には表示されずホバーに表示される。プロット点に表示させるためにはlist
にするか各要素を文字列(string
)にする必要がある。試した内容を以下に示す。
np.round
はnumpy
配列の各要素を四捨五入する関数で、そのまま使用すると桁数が多くなるので四捨五入した。
text=y:
プロットには表示されずホバーに表示されるtext=f"{y}":
配列全体が表示されるtext=np.round(y, 2):
プロットには表示されずホバーに表示されるtext=y.tolist():
プロットに表示されるtext=np.round(y, 2).tolist():
プロットに表示されるtext=np.round(y, 2).astype(str):
プロットに表示される
上のグラフは.astype
を使用した場合のグラフ。各プロット点にその点のy
の値が四捨五入して表示されている。
go.Scattergl
でtext
を使用することは少ないだろうが、go.Scatter
と挙動が異なるので紹介しておく。
import numpy as np
import plotly.graph_objects as go
import plotly.io as pio
# mode='markers+text'でグラフ化
num = 10000
np.random.seed(1)
x = np.random.randn(num)
y = np.random.randn(num)
plot = [
go.Scattergl(
x=x, y=y, mode='markers+text',
marker=dict(color=y, colorscale='jet'),
# text=y # プロットには表示されずホバーに表示される
# text=f"{y}" # 配列全体が表示される
# text=np.round(y, 2), # プロットには表示されずホバーに表示される
# text=y.tolist(), # プロットに表示される
# text=np.round(y, 2).tolist(), # プロットに表示される
text=np.round(y, 2).astype(str), # プロットに表示される
)
]
layout = go.Layout(title='Scattergl')
fig = go.Figure(data=plot, layout=layout)
# グラフ全体とホバーのフォントサイズ変更
fig.update_layout(font_size=20, hoverlabel_font_size=20)
fig.show()
# グラフ保存
prefix = 'go-scattergl' # 保存ファイル名の接頭辞
save_name = f"{prefix}_scattergl_text_{num}"
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")
Scattergl
とScatter
の描画速度を比較
描画処理のみ | グラフ表示のみ | グラフ保存のみ | グラフ表示後に保存 | |||||
---|---|---|---|---|---|---|---|---|
回数 | Scattergl |
Scatter |
Scattergl |
Scatter |
Scattergl |
Scatter |
Scattergl |
Scatter |
1 | 0.307774 | 0.32089488 | 1.677471 | 2.18238825 | 14.4267231 | 31.0453515 | 15.77174 | 31.7769413 |
2 | 0.31865 | 0.33095667 | 2.535154 | 3.35514833 | 29.1333956 | 66.1240122 | 33.72014 | 63.826073 |
3 | 0.327651 | 0.34137896 | 3.639962 | 4.520175 | 42.0895075 | 99.0547675 | 49.40739 | 93.0411114 |
4 | 0.336292 | 0.35187017 | 4.63847 | 5.869156 | 55.382133 | 131.53646 | 64.5005 | 124.074986 |
5 | 0.346516 | 0.36271729 | 5.578106 | 8.00801662 | 69.3545265 | 159.908457 | 79.13958 | 157.548764 |
6 | 0.356704 | 0.37197 | 6.517531 | 10.083821 | 84.4228483 | 188.999129 | 93.21975 | 188.945668 |
7 | 0.369706 | 0.38283708 | 7.533555 | 13.4813174 | 100.675488 | 224.305383 | 108.5783 | 219.811906 |
8 | 0.380136 | 0.39143146 | 8.49465 | 18.9919425 | 116.801716 | 254.365836 | 122.4507 | 252.816432 |
9 | 0.388507 | 0.39984108 | 9.499124 | 28.7700106 | 134.09169 | 284.401813 | 137.7134 | 286.390838 |
10 | 0.397073 | 0.40892542 | 10.48082 | 36.375247 | 147.310917 | 316.606768 | 154.3879 | 317.16722 |
最後にScatter
とScattergl
で処理・描画・保存の速度を比較してみる。今回は以下の4パターンでScatter
、Scattergl
の処理終了までの時間を計測した。
グラフ保存で保存の許可を求められた際には放置し、自然に許可されるようにした。手動で許可してしまうと反応タイミングで時間の差異が生まれてしまうという判断だ。
- グラフを描画のみ(
fig=
まで) - グラフ保存のみ
- グラフ表示のみ
- グラフ表示後に保存
使用したコードは以下。それぞれ20万データの散布図を10回ループさせてみた。この検証以外にも色々な方法があると思うが、今回は簡単にこれで進める。なお、go.Scatter
版は最後にも載せるが、以下のコードのgl
がない版だ。
また、グラフ描画のみの場合などグラフ表示処理が不要な時はその部分のコードは削除して実行した。
import numpy as np
import time
import plotly.graph_objects as go
import plotly.io as pio
# グラフの描画時間を比較
start_time = time.perf_counter()
# Scatterglでグラフの描画と表示の時間を測定
time_lst = []
for loop in range(10):
num = 200000 # 20万データ
np.random.seed(loop)
x = np.random.randn(num)
y = np.random.randn(num)
plot = [
go.Scattergl(
x=x, y=y, mode='markers',
marker=dict(color=y, colorscale='jet'),
)
]
fig = go.Figure(data=plot)
fig.show() # 描画処理のみ・保存のみの場合は不使用
# グラフ保存。描画処理のみ・グラフ表示のみの場合は不使用
prefix = './scattergl/go-scattergl' # 保存ファイル名の接頭辞
save_name = f"{prefix}_{loop}"
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")
# 作成したグラフにHTMLコードを保存
html_code = fig.to_html(include_plotlyjs='cdn', full_html=False)
with open(f"{save_name}.txt", mode='w') as f:
f.write(html_code)
execution_time = time.perf_counter() - start_time
time_lst.append(execution_time)
print(time_lst)
4パターンでgo.Scatter
、go.Scattergl
の処理時間を比較した表がこの章の最初のに示したものだ。単位は秒。描画処理のみの場合は実行時間にそこまでの差は生まれなかったが、グラフ表示と保存処理が加わるとその違いは顕著に現れた。
今回は20万データの10ループだが、グラフ表示後に保存する処理では2倍程度の差が生まれた。なのでやはり大量のデータを扱う際にはgo.Scattergl
を使用するのがよさそうだ。
Scattergl
なら大量データでも簡単にグラフ化できる
今回はPythonのPlotlyで大量のデータを軽く簡単に扱えるgo.Scattergl
を紹介した。WebGL方式で大量のデータでもほとんど引っ掛かりなく動作することができる。
Pytthonはデータ解析やグラフ化が得意なので多くのデータを扱うだろうが、その視覚化に適しているだろう。
ただし、少ないデータの場合は通常のgo.Scatter
を使う方が他の人が見ても理解しやすい。go.Scatter
は以下の記事で解説している。
-
【Plotlyで散布図】go.Scatterのグラフの描き方まとめ
これからPloltyで動くグラフを作りたい、もっとグラフをキ ...
続きを見る
一方でgo.Scattergl
を使う場合は以下と考えている。参考にしていただきたい。
go.Scatterglを使うタイミングの目安
- 10,000データを超えるグラフ
- グラフの動きがモッサリしてきた
- 見栄えより動作が重要
これまで大量のデータを扱う際にデータの重さで悩んでいた人は是非go.Scattergl
を使ってほしい。
関連記事
-
【Plotly&ボタン】updatemenusとbuttonsでボタン機能を追加
Plotlyはプロットしたデータを動かすことができるのが大き ...
続きを見る
-
【Plotly&ボタン】updatemenusのargs2で2回目のボタン押下機能を追加
今回はPlotlyのボタン機能に2回目のボタン押下の処理を追加& ...
続きを見る
-
【Plotly&sliders】スライダーを追加しデータを切り変える
本記事ではPlotlyでデータの流れを簡単に理解できる機能の ...
続きを見る