【Bokehを学ぶ-第3回】Bokehのグラフの細かい部分を整える 凡例、テキスト、注釈

Python

皆さん、こんにちは!

この記事はPythonのデータ可視化ライブラリである「Bokehを学ぶシリーズ」の第3回目。

公式ドキュメントのFirst Stepを更に詳しく解説するようなイメージで進めていきます。

朱莉
Bokehを使って、データ分析の結果を多くの人に共有しましょう!!

この記事では、第1回目ということで、Bokehとは何なのか触れつつ、公式ドキュメントのFirst Stepの1つ「Adding legends, text, and annotations」を解説していきます。

シリーズの別記事を以下にまとめておきます!

【Bokehを学ぶ-第1回】Bokehでインタラクティブな可視化を行おう! 折れ線グラフの描画

【Bokehを学ぶ-第2回】Bokehで複数種類のグラフを描画しよう

凡例の追加と細かい調整

凡例は、グラフ要素(circleやscatterなど)を定義する際に、legend_label変数を有効化することで自動的に定義されます。

以下のように、凡例の名称を定義するのみで、無難な感じで画面右上に表示されます。

p.circle(x, y3, legend_label="Objects")

ただ、これでは味気ないですし、テキストのサイズをカスタマイズしたり、フォントや色身を他のツールと揃えたいこともあるでしょう。

この時、オブジェクト(今回であれば ”p”)のlegendプロパティを用いて後からカスタマイズすることが可能です。

from bokeh.plotting import figure, show

# サンプルデータを作成
# x,yをそれぞれリスト形式
x = [1, 2, 3, 4, 5]
y1 = [4, 5, 5, 7, 2]
y2 = [2, 3, 4, 5, 6]

# 新しいプロットの作成
p = figure(title="Legend example")

# 折れ線グラフを作成する
# タイトル、軸ラベルを決定し、figureインスタンスを立ち上げる
line = p.line(x, y1, legend_label="Temp.", line_color="blue", line_width=2)
# 折れ線グラフを作成する
# タイトル、軸ラベルを決定し、figureインスタンスを立ち上げる
circle = p.circle(
    x,
    y2,
    legend_label="Objects",
    fill_color="red",
    fill_alpha=0.5,
    line_color="blue",
    size=80,
)

### 凡例をカスタマイズする###

# 凡例の場所を左上部に調整
p.legend.location = "top_left"

# 凡例のタイトルを設定
p.legend.title = "Obervations"

# 凡例のテキストに関する部分を調整
p.legend.label_text_font = "times" # フォントを変更
p.legend.label_text_font_style = "italic" # イタリック調に変更
p.legend.label_text_color = "navy" # テキストの色を変更

# 凡例の枠線や背景色などを変更
p.legend.border_line_width = 3 # 縁線を変更
p.legend.border_line_color = "navy" # 縁線の色を変更
p.legend.border_line_alpha = 0.8 # 縁線の透明度を変更
p.legend.background_fill_color = "navy" # 凡例の背景色を変更
p.legend.background_fill_alpha = 0.2 # 凡例の透明度を変更

# 結果を描画する
show(p)

上記の例では、凡例のフォントやイタリック、色味などを変更しています。

こちらを自身でカスタマイズすることで、他のソフトと雰囲気を揃えたり、対外的に出す資料としての体裁を整えま

見出しのテキストスタイルを整える

見出しは、これまでのサンプルコードでも記述してきました。

figure関数にtitle引数を渡すことでタイトルを設定することができます。

p = figure(title="Headline example")

タイトルももちろん、フォントや色など様々カスタマイズすることが可能です。

from bokeh.plotting import figure, show

# サンプルデータを作成
# x,yをそれぞれリスト形式
x = [1, 2, 3, 4, 5]
y = [6, 7, 2, 4, 5]

# 新しいプロットの作成
p = figure(title="Headline example")

# 折れ線グラフを作成する
p.line(x, y, legend_label="Temp.", line_width=2)

# 凡例の場所を左側に移す
p.title_location = "left"

# タイトル名を変更する
p.title.text = "Changing headline text example"

# タイトルを調整する
p.title.text_font_size = "25px" # フォントを変更
p.title.align = "right" # タイトルを右に強制
p.title.background_fill_color = "darkgrey" # タイトルの背景色を変更
p.title.text_color = "white" # タイトルの文字色を変更

# 結果を描画する
show(p)

軸名を調整する(LaTeX表記、MathML表記)

Bokehでは、軸ラベルや目盛ラベルにLaTeXやMathMLといった数式を記述することも可能です。

LaTeXやMathMLは、MathJaxライブラリを使用して処理を行うため、記述方法の詳細は、MathJaxの公式ドキュメントをご確認ください。

LateX表記

LaTex表記を軸ラベルに使用する場合、MathJaxの文法(r”○○”)で記述する必要があります。

各軸の記述を変更する場合、plot.yaxis.axis_label()のように、axis_labelを用いる必要があります。

※ライブラリのインポートなどは不要です。

試しにCos関数を描画してみます。

from numpy import arange, pi, cos
from bokeh.plotting import figure, show

x = arange(-2*pi, 2*pi, 0.1)
y = cos(x)

plot = figure(height=200)
plot.circle(x, y, alpha=0.6, size=7)

plot.yaxis.axis_label = r"\[\cos(x)\]"
plot.xaxis.axis_label = r"\[x\cdot\pi\]"

show(plot)

このように各軸に数式を適用することができます。

次は軸ラベルではなく、軸に直接LaTeXを埋め込んでみます。

軸に直接埋め込みを行う場合、すこし面倒ですが文字列での記述になるため、全てユーザー側で指定してやる必要があります。

major_label_overrides関数が必要で、軸ラベルの元の値に対応したテキストを辞書型で定義します。

from numpy import arange

from bokeh.plotting import figure, show

x = arange(1, 4.5, 0.25)
y = 1 / x

plot = figure(height=200)
plot.circle(x, y, fill_color="blue", size=5)
plot.line(x, y, color="darkgrey")

plot.xaxis.axis_label = "Resistance"
# X座標の元の値に相当
plot.xaxis.ticker = [1, 2, 3, 4]
plot.yaxis.axis_label = "Current at 1 V"

# X軸をそれぞれ辞書型で定義
plot.xaxis.major_label_overrides = {
    1: r"$$1\Omega$$",
    2: r"$$2\Omega$$",
    3: r"$$3\Omega$$",
    4: r"$$4\Omega$$",
}

show(plot)

グラフ内ではなく、単純にテキストを記述したい場合もあるでしょう。

その場合は、Div関数(HTMLでいうDiv要素をBokehで操作できるもの)を用いて以下のように記述します。

もちろんLaTexにも対応しているので、数式を載せたりすることも可能です。

from bokeh.io import show
from bokeh.models import Div

div = Div(
    width=400, height=100, background="#fafafa",
    text="BokehでLaTexを書こう!! $$\sin^2(x) + \cos^2(x) = 1$$"
)
show(div)

グラフに注釈(Annotation)を設定する

グラフの一部分を視覚的に強調することも可能です。

このチュートリアルでは最も基本的な強調方法である、ボックスアノテーションを用います。

基本的には、「グラフのプロパティ(p = figure(○○))にアノテーションを上書きする」イメージです。

順番としては、グラフを定義した後から、アノテーションを加えます。

ボックスアノテーションでは、BoxAnnotationクラスをインポートして用います。

from bokeh.models import BoxAnnotation

以下では、グラフを3つのエリアに分けてアノテーションを作成します。

low_box = BoxAnnotation(top=20, fill_alpha=0.2, fill_color="#F0E442")
mid_box = BoxAnnotation(bottom=20, top=80, fill_alpha=0.2, fill_color="#009E73")
high_box = BoxAnnotation(bottom=80, fill_alpha=0.2, fill_color="#F0E442")

import random

from bokeh.models import BoxAnnotation
from bokeh.plotting import figure, show

# サンプルデータを作成
# xは0から50まで、yはランダムで作成
x = list(range(0, 51))
y = random.sample(range(0, 100), 51)

# 新しいプロットの作成
p = figure(title="Box annotation example")

# 折れ線グラフを作成
line = p.line(x, y, line_color="#000000", line_width=2)

# 矩形の注釈を追加
low_box = BoxAnnotation(top=20, fill_alpha=0.2, fill_color="#F0E442")
mid_box = BoxAnnotation(bottom=20, top=80, fill_alpha=0.2, fill_color="#009E73")
high_box = BoxAnnotation(bottom=80, fill_alpha=0.2, fill_color="#F0E442")

# add boxes to existing figure
p.add_layout(low_box)
p.add_layout(mid_box)
p.add_layout(high_box)

# 結果を描画する
show(p)
余談ですが、Bokehのグラフは、”レイアウト”と”プロット”が完全に分かれています。
アノテーションは”レイアウト”の方に含まれるので、データをプロットした後に、
アノテーションを追加しても、必ず”プロット”の後ろに色を入れてくれるので、
順番を気にする必要がなく便利です。

Matplotibだと、コードの実行順にレイヤーが決まってしまうので、順番にも気を使う必要が出てしまいます。

まとめ

「Bokehを学ぶシリーズ」第3回はいかがでしたでしょうか。

まずはBokehは簡単にデータを可視化出来るだけでなく、かゆいところに手が届く非常に優秀なライブラリです。

経った数行でイイ感じのグラフを書くことが出来るBokehをマスターできれば、簡単に社内共有用のデータダッシュボードを作成することができそうです。

朱莉
このシリーズを通じて、データ可視化をより効率的に行えるようになれば嬉しいです!