今回は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 numpy as np
import time
import plotly.graph_objects as go
import plotly.io as pio
print('------------------------------------------------------------')
# 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")
print('------------------------------------------------------------')
# 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")
print('------------------------------------------------------------')
# 100万データをScatterglでプロットする
num = 1000000
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")
print('------------------------------------------------------------')
# 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")
print('------------------------------------------------------------')
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)
下準備の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
だと動作がかなりもっさりする