BokehでInteractiveに描画するための基本


早速アヤメのデータセットを使って散布図を描画してみたいと思います。

アヤメのデータセット

ソースコード

import pandas as pd
from sklearn.datasets import load_iris
from bokeh.plotting import figure, show

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

p = figure(title="Iris", plot_width=400, plot_height=400)
p.circle(x=df["sepal length (cm)"], y=df["sepal width (cm)"])

show(p)

まずは必要なライブラリをインポートして、処理に入ります。アヤメのデータセットをロードしてpandas.DataFrame形式にします。

そのあと、Bokehのライブラリを読んでおり、"figure"というメソッドを読んで、ここでは描画タイトル、幅と高さを指定しています。

その後、"p.circle"というメソッドで散布図を描画しています。ここでは最低限の引数しか指定していませんが、散布図のx軸とy軸にDataFrameから列を指定してデータを渡しています。

最後に"show(p)"のメソッドが呼び出されると、自動的にHTMLが出力され、Webブラウザが起動し、描画結果を確認することができます。

右側のツールボックスはデフォルトで利用できます。上から説明しますと、

Pan:ドラッグして描画結果を動かすことができます。
Box Zoom:左クリックで範囲指定して拡大します。
Wheel Zoom:マウスのホイールで拡大縮小します。
Save:描画結果をpngファイルで保存します。(もし描画結果を動かしていたり、拡大縮小している場合はその状態が保存されます)
Reset:描画当初の状態に戻ります。

その他、描画するチャートの種類に従ってそれに合うツールを個別に設定したり、不要なツールを削除することも可能です。ここは追ってご説明します。

上記までは、実装する上ではMatplotlibとも特に変わった点は無いかと思います。ここからBokehならではの実装に入っていきます。

アヤメのデータセットには、sepalとpetalがあります。これをInteractiveにデータを入れ替えるボタンを実装してみます。

import pandas as pd
from sklearn.datasets import load_iris
from bokeh.plotting import figure
from bokeh.models import ColumnDataSource
from bokeh.models.widgets import Button
from bokeh.layouts import Column, Row
from bokeh.io import curdoc

iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)

sepal_button = Button(label="sepal", button_type="success")
petal_button = Button(label="petal", button_type="success")

source = ColumnDataSource(data=dict(length=[], width=[]))

def update_by_sepal():
    source.data = dict(
        length=df["sepal length (cm)"],
        width=df["sepal width (cm)"]
    )

def update_by_petal():
    source.data = dict(
        length=df["petal length (cm)"],
        width=df["petal width (cm)"]
    )

sepal_button.on_click(update_by_sepal)
petal_button.on_click(update_by_petal)

p = figure(title="Iris", plot_width=400, plot_height=400)
p.circle(x="length", y="width", source=source)

button_area = Column(sepal_button, petal_button)
layout = Row(button_area, p)

curdoc().add_root(layout)

描画結果(初期状態)

「sepal」ボタン押下

「petal」ボタン押下

それではソースコードの解説をしていきます。

sepal_button = Button(label="sepal", button_type="success")
petal_button = Button(label="petal", button_type="success")

ここでは、ボタンのオブジェクトを初期化しています。その際、labelという引数でボタン上に表示させる文字列を指定して、button_type="success"とすると、緑色のボタンになります。これは特にさほど重要ではありませんのでここでは詳細は省きます。

source = ColumnDataSource(data=dict(length=[], width=[]))

ColumnDataSourceというオブジェクトが登場します。BokehでInteractiveに描画をする際に最も重要なオブジェクトです。このColumnDataSourceの中にdataというフィールドがあり、この中身を切り替えることで描画の切り替えを制御しています。ここではまず初期化のためにデータは空の配列を渡しています。

def update_by_sepal():
    source.data = dict(
        length=df["sepal length (cm)"],
        width=df["sepal width (cm)"]
    )

def update_by_petal():
    source.data = dict(
        length=df["petal length (cm)"],
        width=df["petal width (cm)"]
    )

各ボタンを押下した際に実行するメソッドを定義しています。update_by_sepalでは、先ほど初期化したColumnDataSourceオブジェクトのdataフィールドにsepalに関連するカラムのデータを渡しています。update_by_petalでは、petalに関連するカラムのデータを渡しています。

sepal_button.on_click(update_by_sepal)
petal_button.on_click(update_by_petal)

ボタンにcallbackを実装します。callbackとは、メソッドの引数に別のメソッドを指定することを指します。ここでは、on_clickというメソッドにupdate_by_xxxxxが指定されています。これにより、sepal_buttonが押下された際にはupdate_by_sepalが、petal_buttonが押下された際にはupdate_by_petalが呼び出されるようになります。

p = figure(title="Iris", plot_width=400, plot_height=400)
p.circle(x="length", y="width", source=source)

pの初期化については特段変わりありませんが、circleメソッドの引数の指定の仕方がInteractiveにする場合は異なってきます。ここで、ColumnDataSourceオブジェクトで指定した辞書のキーをxとyの引数に文字列で指定します。そして、sourceという引数にColumnDataSourceオブジェクトを渡してやります。これにより、描画結果がColumnDataSourceオブジェクト内のdataフィールドの切り替えに伴って、描画結果が切り替わるようになります。

button_area = Column(sepal_button, petal_button)
layout = Row(button_area, p)

ボタン2つと描画結果をレイアウトします。Columnというクラスは縦にレイアウト、Rowというクラスは横にレイアウトします。これにより、ボタン2つが縦に並んで、そのボタンエリアと描画結果が横に並んでいます。

curdoc().add_root(layout)

BokehでInteractiveに描画する際には、このcurdoc().add_root(描画対象オブジェクト)という形で指定します。このメソッドの詳細はまた別の機会に説明します。

最後に、このpythonファイルは普通に実行しても描画されません。コマンドラインより以下のように実行します。

bokeh serve --show <ファイル名.py>

そうすると、初期状態の描画結果がWebブラウザ上で表示されます。その際、http://localhost:5006/<ファイル名>でページが起動します。この辺のコマンド周りの詳細もまた別の機会にご説明します。

まず、ここで押さえていただきたいポイントとしては、

ColumnDataSourceオブジェクトのdataフィールドを更新することで描画結果が更新される
描画する際にColumnDataSourceオブジェクトのdataフィールドに渡している辞書のキーと対応するように引数を指定する
callbackの実装によりdataフィールドの更新を行う

この3点になります。これだけでも、例えば、callbackの中でAPIを叩いたり、あるいはデータベースに対するクエリを実行したりして、データを更新して描画結果を切り替えるということができるようになります。

ボタン以外にも様々なオブジェクトやcallbackの実装方法が提供されていますので、その辺もまたの機会にご説明したいと思います。

この記事が気に入ったらサポートをしてみませんか?