こんな人にオススメ
plotly
を使ってグラフを描いているんだけど、データが大量にあるからグラフがモッサリしたり何も表示されなくなってしまう。
データを減らさずにplotly
のグラフを軽くする方法を教えて!
ということで、今回はplotlyで大量のグラフを描く方法について解説する。plotly
はグラフをグリグリ動かせるからかなり重宝するんだけど、そのせいか重い。しかもデータが増えるとその分もっと重くなる。
かといって機能を省いて貧相なグラフにすると負けた感じがある。それに一旦グラフを作成しただけでPCのファンが爆速で回るのもの負けた感。
そこで使えるがWebGLなるもの。WebGLってのはどうやらブラウザで3D表示を高速にするための仕様のようで、GPUを呼び出したりして高速にしているよう。これをplotly
でも使用することで大量のデータを軽く扱える。
python環境は以下。
- Python 3.9.6
- numpy 1.21.1
- plotly 5.1.0
下準備
import numpy as np import plotly.graph_objects as go import plotly.io as pio import time import functions
まずは下準備としてのimport
関連。今回はWevGLを使用することでどれくらい処理時間が短縮されるのかも測りたい。よって、time
もimport
。
さらに、共通して使用するような処理を一括管理するために以下のコードがあるfunctions.py
も作成。同じディレクトリにある場合はそのままimport
できる。
import sys import numpy as np sys.path.append('../../') import plotly_layout_template as template def set_func(func, x: np.array, y: np.array, name: str): d = func( mode='markers', x=x, y=y, name=name, ) return d def config(): return template.plotly_config()
一方で、異なるディレクトリに読みたいファイルがある場合はいつも通りsys
でディレクトリを移動して読み込んでいる。plotly_layout_template
は自作テンプレートで以下に詳しい内容が書いてある。
-
-
【随時更新 備忘録】plotlyのグラフ即席作成コード
続きを見る
ファイル構造
├── functions.py ├── heatmap.py ├── heatmapgl.py ├── passed_time.py ├── scatter.py └── scattergl.py
本記事で使用するファイルは上のような構造になっている。今回は処理時間を測りたいので、それぞれの処理ごとにファイルを分けて、それぞれのファイルごとにtime
ライブラリの関数で時間を計測。
functions.py
: 共通して使用する処理が存在heatmap.py
:go.Heatmap
を使用して2次元マップを作成heatmapgl.py
:go.Heatmapgl
を使用してWebGL適用後の2次元マップを作成passed_time.py
: WebGLを使用する前後での処理時間計測用scatter.py
:go.Scatter
を使用して散布図を作成scattergl.py
: go.Scatterglを使用してWebGL適用後の散布図を作成
要するに散布図と2次元マップでWebGLを使用するか否かを分けて、その処理時間を比較するということ。本当は他にもWebGL系はあるけど今回はこの2つに絞る。より詳しい内容は公式のここに載っている。
plotly
のScatter
とHeatmap
についてはそれぞれ以下参照。
-
-
【plotly&go.Scatter】plotlyの散布図グラフの描き方
続きを見る
-
-
【plotly&heatmap】go.Heatmapで2次元配列をマップ化
続きを見る
大量のデータをグラフ化するとモッサリ
そもそも本記事はなんで書かれたのかという点だが、データ数が多いとグラフが重くなる。データ数だけじゃなくて、ボタンなどの付加機能も重くなる大きな原因だろう。
だからと言ってデータを削減したり機能を省略するというのはもったいないし、作成した時間が無駄。という事で、現状維持のままどうにかグラフを軽くできないかということ。
そんな上手い話があるのかという事だがあった。それがWebGLを使用するという方法。
Scatter
の場合はScattergl
まずは散布図から。上の画像のように大量にプロット点があるとかなりモッサリしてPCにも負担が大きいし何よりストレスが大きい。
go.Scatter
を使用
import numpy as np import plotly.graph_objects as go import plotly.io as pio import time import functions start_time = time.perf_counter() def set_plot(number: int): # 標準正規分布(平均0、分散1)の乱数を生成 # data_num = 100000 data_num = 10000 # 記事用 plot = [] for num in range(number): np.random.seed(num) x = np.random.randn(data_num) y = np.random.randn(data_num) d = functions.set_func(func=go.Scatter, x=x, y=y, name=f"data {num}") plot.append(d) return plot plot_num = 10 fig = go.Figure(data=set_plot(plot_num)) fig.show() execution_time = time.perf_counter() - start_time print(execution_time) name = f"scatter{plot_num}" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{name}.html", config=functions.config(),) pio.write_image(fig, f"{name}.png")
WebGLを使用せずに散布図を作成する場合は上のようなコードになる。計測時間はimport
が終わってからプロットしたグラフを表示するまでとした。time.perf_counter()
を使用するとより正確な時間となるらしい。
グラフを静止画で保存するときに認証とかがあるからそれを避けるためにグラフ表示までの時間計測にしている。plotly
のグラフ保存については以下。
-
-
【plotly&orca】plotlyで静止画保存(orca)
続きを見る
時間計測の結果は後ほど述べるとして、出来上がるグラフは以下の感じ。ただし、記事にする時の容量の都合上、データ数は1,000に制限した。処理時間計測時にはデータ数を100,000に増やして行う。
また、plot_num = 10
でプロット種類は10にした。閲覧しているブラウザによって違いはあるだろうけど、多少のモッサリ感があるだろう。
go.Scattergl
を使用
import numpy as np import plotly.graph_objects as go import plotly.io as pio import time import functions start_time = time.perf_counter() def set_plot(number: int): # 標準正規分布(平均0、分散1)の乱数を生成 # data_num = 100000 data_num = 1000 # 記事用 plot = [] for num in range(number): np.random.seed(num) x = np.random.randn(data_num) y = np.random.randn(data_num) d = functions.set_func(func=go.Scattergl, x=x, y=y, name=f"data {num}") plot.append(d) return plot plot_num = 10 fig = go.Figure(data=set_plot(plot_num)) fig.show() execution_time = time.perf_counter() - start_time print(execution_time) name = f"scattergl{plot_num}" pio.orca.config.executable = '/Applications/orca.app/Contents/MacOS/orca' pio.write_html(fig, f"{name}.html", config=functions.config(),) pio.write_image(fig, f"{name}.png")
さっきのgo.Scatter
をWebGLを使用して軽くしたのが上のコード。変更点は使用する関数がgo.Scattergl
になっただけ。あとはファイル名。こうするだけで以下のように軽い散布図を作成することができる。