数値計算をブラウザでリアルタイムプロットしたい
bokehを利用していい感じのリアルタイムプロットが出来るようになったのでメモ
jsを用いてやろうとしていましたが、あまりうまくいかなかったので少し回り道してみました
コード全文
こちらを実行すれば動きます
# myplot.py
from bokeh.plotting import figure, curdoc
from bokeh.driving import linear
import numpy as np
import random
p = figure(plot_width=400, plot_height=400)
######重要###########
p.x_range.follow="end"
p.x_range.follow_interval = 20
p.x_range.range_padding=0
#####################
r1 = p.line([], [], color="firebrick", line_width=2)
r2 = p.line([], [], color="navy", line_width=2)
ds1 = r1.data_source
ds2 = r2.data_source
@linear()
def update(step):
ds1.data['x'].append(step)
ds1.data['y'].append(np.sin(step/10))
ds2.data['x'].append(step)
ds2.data['y'].append(np.cos(step/10))
ds1.trigger('data', ds1.data, ds1.data)
ds2.trigger('data', ds2.data, ds2.data)
curdoc().add_root(p)
# Add a periodic callback to be run every 500 milliseconds
curdoc().add_periodic_callback(update, 100)
解説
必要なライブラリを用意
from bokeh.plotting import figure, curdoc
from bokeh.driving import linear
import numpy as np
import random
figureを定義(400×400)
p = figure(plot_width=400, plot_height=400)
x軸の設定(重要)
p.x_range.follow="end"
p.x_range.follow_interval = 20
p.x_range.range_padding=0
follow...x軸をある値で追従する。今回はendなので、最大値を追従。
interval...ストリーミング間隔
padding...ストリーミング速度を加える?今回は0なので特に意味なし。
documents
https://bokeh.pydata.org/en/latest/docs/reference/models/ranges.html
データフレームの生成
r1 = p.line([], [], color="firebrick", line_width=2)
r2 = p.line([], [], color="navy", line_width=2)
ds1 = r1.data_source
ds2 = r2.data_source
p.line()...figureの".line(x,y,...)"メソッドを利用することで線を描写(pはfigureのインスタンス)
r1.data_source...生成したデータセットをここにぶち込めばいいみたい
デコレータ
@linear()
@linear...decoratorというらしい。連続データを生成している?以下のドキュメントを参照してください。おそらくですが、bokeh内ですでにlinearという関数が定義されていて、@linearで呼び出して続く関数を修飾しているのだと思われます。
おすすめ:Pythonのデコレータについて(Qiita)
https://qiita.com/mtb_beta/items/d257519b018b8cd0cc2e
デコレータのdocument(Bokeh)
http://bokeh.pydata.org/en/0.11.1/docs/reference/driving.html
データの生成&更新
def update(step):
ds1.data['x'].append(step)
ds1.data['y'].append(np.sin(step/10))
ds2.data['x'].append(step)
ds2.data['y'].append(np.cos(step/10))
ds1.trigger('data', ds1.data, ds1.data)
ds2.trigger('data', ds2.data, ds2.data)
updateという関数を定義します。引数はstepで。triggerはよくわかりません。
trigger("名前", x, y)のような感じ?
ブラウザに反映
curdoc().add_root(p)
# Add a periodic callback to be run every 500 milliseconds
curdoc().add_periodic_callback(update, 100)
現在のPを返す
curdoc().add_root(p)...現在のfigureを表示
curdoc().add_periodic_callback(update, 100)...updateを実行して、100msでコールバック
curdoc()のdocument
https://bokeh.pydata.org/en/latest/docs/reference/io.html
感想
やはりpythonで完結しているものは理解しやすいです。jsの勉強もコツコツ頑張ります。