Jupyter本の没原稿集 #1 Bokehで凝った円グラフを描画する

ボツネタとは

Jupyter本 にはページ数の都合で入り切らなかった内容を紹介する企画第一弾です。
涙をのみながらボツにした原稿を少しカジュアルにして本ブログで復活させます。

Bokeh で凝った円グラフを描画する

今回は Bokeh を使用して、下記の要件を満たす円グラフを描画してみます。

  • 要素数が多いため、上位のみを表示する
  • 上位以外はテキストを表示せず、目立たない表示にする(徐々にグレイアウト)
  • テキストの書式を設定する

テンプレどおりのグラフなんてつまらない、プレゼンで差をつけたい、なんて場合にはカスタマイズしたグラフに挑戦してみましょう。

In [1]:
import os
import pandas as pd
from bokeh.plotting import figure, output_notebook, show
from numpy import pi, sin, cos
from bokeh.palettes import d3, gray


visible = 10  # 円グラフに表示する数

output_notebook()

# データを取得
base_url = 'https://raw.githubusercontent.com/practical-jupyter/sample-data/master/anime/'
df = pd.read_csv(os.path.join(base_url, 'anime_split_genre.csv'))

# genre列を集計し、members列の値が大きい順にソート
genre = df.groupby('genre')['members'].sum().sort_values(ascending=False)
# パーセントに変換
genre = genre.map(lambda x: x / genre.sum())

colors = d3['Category20b'][20]


def plot_wedge(plot, pct, start_angle=0, style={'color': '#FFFFFF'},
               radius=1):
    # 割合をラジアンに変換
    end_angle = start_angle + pct * 2 * pi
    # 円グラフ部分をプロット
    plot.wedge(
        x=0, y=0, radius=radius,
        start_angle=start_angle, end_angle=end_angle,
        **style)
    return end_angle


def plot_text(plot, pct, text, start_angle=0, style={'text': None}, radius=1):
    end_angle = start_angle + pct * 2 * pi
    # テキストの座標
    r = start_angle + pct * pi
    x = cos(r) * radius * 0.7
    y = sin(r) * radius * 0.7
    # テキストを表示
    plot.text(x=[x], y=[y], text=[text], **style)
    return end_angle


title = 'メンバ数が多いジャンルトップ{}'.format(visible)
p = figure(
    plot_width=400, plot_height=400, title=title,
    x_range=(-1, 1), y_range=(-1, 1))
p.title.align = 'center'

# 枠、罫線、軸を全て非表示
p.outline_line_color = None
p.grid.visible = False
p.axis.visible = False

# 円グラフの描画
next_start_angle = 0
for i, g in enumerate(genre):
    if i + 1 > visible:
        wedge_color = gray(len(genre))[i]
    else:
        wedge_color = colors[i]
    next_start_angle = plot_wedge(
        p, g, next_start_angle, style={'color': wedge_color})

# テキストを表示
# 円グラフでテキストを被せてしまわないよう、表示処理を後から実施
next_start_angle = 0
for i, g in enumerate(genre):
    text = '{0}: {1:.2f}%'.format(genre.index[i], g * 100)
    if i + 1 > visible:
        text_color = None
    else:
        text_color = '#FFFFFF'
    next_start_angle = plot_text(
        p, g, text, next_start_angle,
        # テキストの書式
        style={
            'text_align': 'center',
            'text_baseline': 'top',
            'text_font_size': '9pt',
            'text_font_style': 'bold',
            'text_color': text_color
        })

show(p)
Loading BokehJS ...

解説

円グラフを描画する手順は下記のとおりです。

  1. wedge() メソッドで扇型の図形を描画していく
  2. 1.に塗りつぶしの色を設定する
  3. text() メソッドで内訳のテキストを表示する

たったこれだけなのですが、上記のコードでは書式の設定など、細かいパラメータが指定されています。
Jupyter本 の7章で詳しく解説しています。興味があるかたは参照してみてください。

  • wedge() メソッド
    7-1 さまざまな図形の描画
  • text() メソッド
    7-5 テキストの調整
  • 色の設定
    7-3 色の調整

データの集計方法などは3章にて解説しています。

  • データのソート
    3-6 データ処理
  • データの集計
    3-8 クロス集計