0
0

bokehとは

Bokehは、Pythonでインタラクティブなグラフやビジュアライゼーションを作成するためのライブラリです。

インタラクティブ性や多様なプロットタイプ,カスタマイズ可能性に着目し勉強してみました。

折れ線グラフ

こんな感じのグラフが作成できます
スクリーンショット 2024-07-12 19.46.54.png

下記はコードです。

import pandas as pd
import numpy as np
from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource

# サンプル時系列データを生成
dates = pd.date_range(start="2022-01-01", periods=100, freq="D")
data1 = np.cumsum(np.random.randn(100))
data2 = np.cumsum(np.random.randn(100))

# データをDataFrameに変換
df = pd.DataFrame({'date': dates, 'data1': data1, 'data2': data2})

# BokehのColumnDataSourceを作成
source = ColumnDataSource(df)

# HTML出力ファイルを設定
output_file("time_series_plot.html")

# Figureを作成
p = figure(title="Sample Time Series Data", x_axis_label='Date', y_axis_label='Values', x_axis_type='datetime', width=800, height=400, toolbar_location=None, background_fill_color="#fafafa")

# データをプロット
p.line('date', 'data1', source=source, legend_label="Data1", line_width=2, color="blue")
p.line('date', 'data2', source=source, legend_label="Data2", line_width=2, color="red")

# レジェンドの位置を設定
p.legend.location = "top_left"

# グラフを表示(HTMLファイルに保存)
show(p)

下記のように2つのグラフ間を塗りつぶしたりできます。
スクリーンショット 2024-07-12 19.54.16.png

import pandas as pd
import numpy as np
from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource

# サンプル時系列データを生成
dates = pd.date_range(start="2022-01-01", periods=100, freq="D")
data1 = np.cumsum(np.random.randn(100))
data2 = np.cumsum(np.random.randn(100))

# データをDataFrameに変換
df = pd.DataFrame({'date': dates, 'data1': data1, 'data2': data2})

# BokehのColumnDataSourceを作成
source = ColumnDataSource(df)

# HTML出力ファイルを設定
output_file("time_series_plot.html")

# Figureを作成
p = figure(title="Sample Time Series Data", x_axis_label='Date', y_axis_label='Values', x_axis_type='datetime', width=800, height=400, toolbar_location=None, background_fill_color="#fafafa")

# データをプロット
p.line('date', 'data1', source=source, legend_label="Data1", line_width=2, color="blue")
p.line('date', 'data2', source=source, legend_label="Data2", line_width=2, color="red")

# 塗りつぶし領域のプロット
for i in range(len(df)-1):
    if df['data1'][i] > df['data2'][i] and df['data1'][i+1] > df['data2'][i+1]:
        # Both points have data1 > data2
        p.patch([df['date'][i], df['date'][i+1], df['date'][i+1], df['date'][i]], 
                [df['data2'][i], df['data2'][i+1], df['data1'][i+1], df['data1'][i]], 
                color="red", alpha=0.5)
    elif df['data1'][i] <= df['data2'][i] and df['data1'][i+1] <= df['data2'][i+1]:
        # Both points have data1 <= data2
        p.patch([df['date'][i], df['date'][i+1], df['date'][i+1], df['date'][i]], 
                [df['data1'][i], df['data1'][i+1], df['data2'][i+1], df['data2'][i]], 
                color="blue", alpha=0.5)
    else:
        # Intersection within this segment
        x0, x1 = df['date'][i], df['date'][i+1]
        y0_data1, y1_data1 = df['data1'][i], df['data1'][i+1]
        y0_data2, y1_data2 = df['data2'][i], df['data2'][i+1]

        # Compute intersection
        x_intersect = x0 + (x1 - x0) * (y0_data1 - y0_data2) / ((y0_data1 - y0_data2) - (y1_data1 - y1_data2))
        y_intersect = y0_data1 + (x_intersect - x0) * (y1_data1 - y0_data1) / (x1 - x0)

        if df['data1'][i] > df['data2'][i]:
            p.patch([x0, x_intersect, x_intersect, x0], 
                    [y0_data2, y_intersect, y_intersect, y0_data1], 
                    color="red", alpha=0.5)
            p.patch([x_intersect, x1, x1, x_intersect], 
                    [y_intersect, y1_data2, y1_data1, y_intersect], 
                    color="blue", alpha=0.5)
        else:
            p.patch([x0, x_intersect, x_intersect, x0], 
                    [y0_data1, y_intersect, y_intersect, y0_data2], 
                    color="blue", alpha=0.5)
            p.patch([x_intersect, x1, x1, x_intersect], 
                    [y_intersect, y1_data1, y1_data2, y_intersect], 
                    color="red", alpha=0.5)

# レジェンドの位置を設定
p.legend.location = "top_left"

# グラフを表示(HTMLファイルに保存)
show(p)

散布図

下記のように散布図を作成することもできます。

スクリーンショット 2024-07-12 23.44.10.png

from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource
import pandas as pd

# サンプルデータを作成
data = {
    'date': pd.date_range('2023-01-01', periods=10),
    'value': [1, 4, 3, 5, 6, 2, 3, 5, 7, 4]
}
df = pd.DataFrame(data)

# ColumnDataSourceを作成
source = ColumnDataSource(df)

# figureを作成
p = figure(x_axis_type="datetime", title="時系列データの散布図", height=400, width=700)

# 散布図を描画
p.circle('date', 'value', size=10, color="navy", alpha=0.5, source=source)

# 出力ファイルを指定
output_file("scatter_plot.html")

# プロットを表示(HTMLファイルに保存)
show(p)
![スクリーンショット 2024-07-12 23.42.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398994/680301d9-4c7c-fea0-4fed-5cd56f4eaf66.png)
![スクリーンショット 2024-07-12 23.43.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/398994/8a1df084-90e1-f0cd-097a-b186b767a133.png)

グラフの中にテキストを入力することができます。

Enter textの入力フォームに文字列を入力しグラフをクリックすると、入力されたテキストがその位置に追加されます。

スクリーンショット 2024-07-12 23.27.17.png

from bokeh.plotting import figure, show, output_file
from bokeh.models import ColumnDataSource, CustomJS, LabelSet
from bokeh.layouts import column
from bokeh.models.widgets import TextInput

# HTMLファイルに出力する設定
output_file("interactive_text_input.html")

# 初期データ
initial_data = dict(x=[1, 2, 3], y=[4, 5, 6], text=['A', 'B', 'C'])
source = ColumnDataSource(data=initial_data)

# プロットの作成
p = figure(width=600, height=600, title="Interactive Text Input Example")

# 初期プロット
p.scatter(x='x', y='y', size=8, source=source)

# ラベルセットの作成
labels = LabelSet(x='x', y='y', text='text', level='glyph', source=source)
p.add_layout(labels)

# JavaScriptコールバック
callback = CustomJS(args=dict(source=source), code="""
    const data = source.data;
    const text = text_input.value;
    const x = cb_obj.x;
    const y = cb_obj.y;
    data['x'].push(x);
    data['y'].push(y);
    data['text'].push(text);
    source.change.emit();
""")

# プロットにタップツールの追加
p.js_on_event('tap', callback)

# テキスト入力ウィジェット
text_input = TextInput(value="Sample Text", title="Enter text:")

# テキスト入力ウィジェットをJavaScriptコールバックに渡す
callback.args['text_input'] = text_input

# レイアウト
layout = column(text_input, p)

# グラフの表示
show(layout)

散布図を時系列順にラインで繋ぐことができます

スクリーンショット 2024-07-12 23.33.23.png

from bokeh.plotting import figure, show
from bokeh.io import curdoc
from bokeh.models import ColumnDataSource
import numpy as np

# 仮データの生成
np.random.seed(0)
dates = np.arange('2020-01', '2023-01', dtype='datetime64[M]')
cpi = np.random.normal(loc=100, scale=5, size=len(dates))
unemployment_rate = np.random.normal(loc=5, scale=0.5, size=len(dates))

# データソースの作成
source = ColumnDataSource(data=dict(
    dates=dates,
    cpi=cpi,
    unemployment_rate=unemployment_rate
))

# プロットの準備
p = figure(title="Phillips Curve", x_axis_label='CPI', y_axis_label='Unemployment Rate')

# 散布図の描画
p.circle('cpi', 'unemployment_rate', size=8, source=source, color='blue', alpha=0.6)

# 線で繋げる
p.line('cpi', 'unemployment_rate', source=source, line_width=2, color='red', alpha=0.6)

# プロットの表示
curdoc().add_root(p)
show(p)

終わりに

今後もこんな感じでbokehでのグラフ作成やデータ分析の発信もしていきます

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0