インタラクティブな可視化が可能なライブラリ「Bokeh」のチュートリアルを試してみた

データアナリティクス事業本部@札幌の佐藤です。
Pythonでグラフ描画ライブラリといえば何を思い浮かべるでしょうか。
たぶん大体の人がMatplotlibを思い浮かべるのではないかと思います。

個人的に描画はBokehを使用しており、Bokehライブラリをおすすめしたいため、チュートリアルの「1.Basic Plotting」と「2.Styling and Theming」からどのような感じの描画が可能か見ていきたいと思います。

Bokeh Tutorial

What's Bokeh

Welcome to Bokeh

上記で記載の通りPythonのグラフ描画ライブラリのひとつです。
特徴としてはインタラクティブな可視化が可能という点です。

Bokehはグラフの拡大縮小や、スライダーなどで動的にグラフを操作することが可能です。
またHTML形式で保存することができるのも特徴のひとつです。勿論画像での保存も可能です。
matplotlibではグラフが画像となるため、そちらと比べても特徴的なライブラリだと思います。

描画元のinputも静的データは勿論、ストリーミングデータも描画することが可能です。

個人的にはグラフもきれいにでるので、そこがお気に入りポイントのひとつです。
Bokeh内でカラーパレットを持っており、描画前に自前でリスト型に定義する必要がありません。

私が対応しておらず触ったことがないですが、RやScalaにも対応しているようです。

PandasのDataFrameをそのまま使用することができるのも利点です。

最新バージョンは本日断面で1.2.0です。

どのような描画が可能かについては以下を確認いただければと思います。
Gallery

また、個人の趣味でアイカツ!の決算短信を以前描画しました。こんな感じの図が描画できます。

実際に描画してみる

まずは基本系の描写ですね。散布図、折れ線、棒グラフです。
今回はJupyter Notebook上でグラフを表示させます。

from bokeh.io import output_notebook, show
from bokeh.plotting import figure 
output_notebook()

ライブラリをimportします。
BokehはJupyter Notebook上にデフォルトで存在しているので、install等は不要です。
ただバージョンが最新ではないので、適宜バージョンを上げてください。
今回は最新の1.2.0で検証しています。

Jupyter Notebook上に描画するため、output_notebook()コマンドを記載しています。

単純なグラフの描画

まず散布図を描画させます。

x=[1, 2, 3, 4, 5]
y=[6, 7, 2, 4, 5]
# グラフ全体の設定を行う
p = figure(plot_width=400,        #描画するグラフの横幅
           plot_height=400,       #描画するグラフの縦幅 
           title="散布図"         #グラフのタイトル 
          )

p.circle(x=x,                   #X軸
         y=y,                   #Y軸
         size=15,               #グラフに対しての直径値 
         line_color="navy",     #円の線の色 
         fill_color="orange",   #円の色
         fill_alpha=0.5         #円の透明度
        )

show(p) # 設定した内容を表示させます。

描画させるための基本的な構成はどんなグラフでも変わりません。
figureでグラフ自体の設定を行い、その引数に対し描画したいグラフの情報を渡します。
最後にshow()でグラフを描画させます。
日本語の表示を意識せずできるのが地味にいいポイントだと思います。

描画した結果として以下のように出力されます。

グラフの右側に表示されているのは、デフォルトで表示されているグラフをインタラクティブに動かすためのツールバーです。(非表示も可能)
以下のように画面を拡大したり、移動させることができます。その情報を維持したまま画像を保存することも可能です。

次に折れ線グラフを描画させます。
複数グラフを同居させたい場合、X軸Y軸が同じであれば、特に意識することなく追加していくだけで実装できます。
Y軸が異なる場合はもう少し実装が必要ですが、別の機会で説明したいと思います。

x1=[1, 2, 3, 4, 5]
y1=[6, 7, 2, 4, 2]
x2=[1, 2, 3, 4, 5]
y2=[1, 2, 3, 4, 5]

#toolbar_locationをNoneにすることで描画時の画面右側のツールバーを消すことができます。
p = figure(plot_width=400, plot_height=400, title="線グラフ", toolbar_location=None)

p.line(x=x1,                  #X軸
       y=y1,                  #Y軸
       line_width=5,          #線の幅
       line_color='#084594',  #線の色
       line_alpha=0.5,        #線の彩度
       legend="折れ線"        #凡例の表示名
      )
p.circle(x1, y1, fill_color="white", size=8)
p.line(x=x2,                  #X軸
       y=y2,                  #Y軸
       line_width=2,          #線の幅
       line_color="green",    #線の色
       line_dash="dashdot",   #線の種類(実線:solid、点線:dashed・dotted・dotdash・dashdot)
       legend="直線"          #凡例の表示名
      )

show(p)

棒グラフも同じように実装できます。 引数がyではなくtopという点が注意です。

x=[1,2,3]
top=[1,2,3]

p = figure(plot_width=400, plot_height=400, title="棒グラフ")

p.vbar(x=x,             #X軸
       width=0.5,       #グラフの幅
       bottom=0,        #Y軸で表示する最小値
       top=top,         #Y軸 
       color="#CAB2D6"  #グラフの色
      )

show(p)

グラフ自体の装飾を変更

グラフ全体の装飾も変更することができます。
グラフの描画と同様にfigureの引数に対し、追加していくだけでOKです。
同じような処理が続くので、グラフ同士を横に並べています。

from bokeh.layouts import row

# グラフ1
p1 = figure(plot_width=400, plot_height=400)
p1.circle([1,2,3,4,5], [2,5,8,2,7], size=10)

p1.xgrid.grid_line_color = None   # X軸のグリッド線を削除

p1.ygrid.band_fill_color = "navy" # Y軸のグリッドの色を指定
p1.ygrid.band_fill_alpha = 0.1    # Y軸のグリッドの色を彩度を指定

# グラフ2
p2 = figure(plot_width=400, plot_height=400)
p2.circle([1,2,3,4,5], [2,5,8,2,7], size=10)

p2.xgrid.grid_line_dash = [6, 5]  # X軸のグリッド線の長さ、点線間の長さを指定
p2.xgrid.grid_line_alpha = 0.5    # Y軸のグリッド線を彩度を指定

p2.ygrid.grid_line_color = None   # Y軸のグリッド線を削除

# グラフを並列に並べる
show(row(p1,p2))

# グラフ1
p3 = figure(plot_width=400, plot_height=400)
p3.asterisk([1,2,3,4,5], [2,5,8,2,7], size=12, color="olive")

p3.xaxis.axis_label = "Temp"     # X軸のラベル名を定義
p3.xaxis.axis_line_width = 3     # X軸の線の太さを指定
p3.xaxis.axis_line_color = "red" # X軸の線の色を指定

p3.yaxis.axis_label = "Pressure"               # Y軸のラベル名を定義
p3.yaxis.major_label_text_color = "orange"     # Y軸のメモリ名の色を指定
p3.yaxis.major_label_orientation = "vertical"  # Y軸のメモリ名の傾きを指定
#傾きはverticalとhorizontal、小数点で定義可能

p3.xaxis.major_label_orientation = -0.5  # X軸のメモリ名の傾きを指定

p3.axis.minor_tick_in = -3 #副目盛りの開始がXY軸からどれだけ離れているか
p3.axis.minor_tick_out = 6 #副目盛りの終了がXY軸からどれだけ離れているか

# グラフ2
p4 = figure(plot_width=400, plot_height=400)
p4.outline_line_color = "red"  # グラフの枠の色
p4.outline_line_width = 1      # グラフの枠の太さ
p4.outline_line_alpha = 0.3    # グラフの枠の彩度

p4.square([1,2,3,4,5], [2,5,8,2,7], size=10)

p4.xaxis.ticker = [1, 1.5, 2, 3.5, 4] # X軸のメモリ値変更
p4.yaxis.bounds = (1, 6)              # Y軸のメモリ範囲変更

show(row(p3,p4))

Pandasを使用した時系列データの描画

最後にPandasのDataFrameを使用して描画したいと思います。 Bokehのサンプルデータを使用し、時系列データの描画を行います。

import bokeh.sampledata
#bokeh.sampledata.download() #サンプルデータのダウンロード
from bokeh.sampledata.glucose import data

data.head()で一部のみの確認ですが、以下のような時系列データを持っています。

ダウンロードしたデータは件数が多すぎるので、一部だけ描画してみます。

week = data.loc['2010-10-01':'2010-10-08'] #データが多すぎるのでDataFrameの一部だけ抽出

p = figure(x_axis_type="datetime",  #X軸のタイプを設定。今回は時系列データのため、datetimeを選択
           plot_height=350, 
           plot_width=800,
           title="Glocose Range"
          )

#DataFrameのカラム名を指定するだけです。
p.line(x=week.index, 
       y=week.glucose
      )

p.xgrid.grid_line_color=None
p.ygrid.grid_line_alpha=0.5
p.xaxis.axis_label = 'Time'
p.yaxis.axis_label = 'Value'

show(p)

x_axis_typeを設定しない場合、グラフ描画自体はうまくいきますが、X軸のラベル表示が正しく行われないので設定が必要です。

最後に

今回は一歩目として比較的簡単な描画をメインに記載しました。
画像の拡大もできるので、時系列データを拡大することで細かくデータの動きを見ることができるようになるのはいいところだなと思います。
まだチュートリアルも残っているので、それらをやりながら紹介していければなと思います。