在 Jupyter/Python 中使用 bokeh 2.3.2 绘制交互式饼图

时间:2021-05-25 20:44:43

标签: python jupyter-notebook callback bokeh

我正在尝试在带有 Bokeh 的 jupyter notebook 中创建饼图,该饼图可以使用滑块进行更新。我有一个自定义函数,可以从预先存在的数据帧创建 data。我希望滑块操纵该函数的输入 f,这样 data 在饼图中显示时是不同的。下面是一个例子:

vals = [20, 100, 50]
names = ["Agnostic", "Competetive", "Loyalist"]

def data_generator(names, vals, f):
    data = pd.DataFrame([[name, val * f] for name, val in zip(names, vals)])
    return data

data = data_generator(names, vals, 10)

data['angle'] = data['value']/data['value'].sum() * 2*pi
data['color'] = Plasma[len(data)]


p = figure(plot_height=350, title="Loyalty Breakout", toolbar_location=None,
           tools="hover", tooltips="@segment: @value", x_range=(-0.5, 1.0))

p.wedge(x=0, y=1, radius=0.4,
        start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
        line_color="white", fill_color='color', legend_field='segment', source=data)

# This is where I need help
update = CustomJS(args=dict(xr=p.x_range), code="""

// I got this JS code from somewhere else. I need this to update the 'f' value in my input funciton.

// JavaScript code goes here

var a = 10;

// the model that triggered the callback is cb_obj:
var f = cb_obj.value;

// models passed as args are automagically available
xr.start = a;
xr.end = b;

""")

lookback_slider = Slider(start=0, end=180, value=30, step=1, title="Lookback (days)")
lookback_slider.js_on_change('value',update)

p.axis.axis_label=None
p.axis.visible=False
p.grid.grid_line_color = None

show(column(lookback_slider, p))

这会创建饼图和滑块,但滑块不起作用。我发现了这个:Using bokeh to plot interactive pie chart in Jupyter/Python 但不幸的是,CustomJS.from_py_func(update) 不起作用,因为 from_py_func 在最新版本的 Bokeh 中已被弃用。谁能帮我用 Bokeh 更新饼图?

1 个答案:

答案 0 :(得分:1)

您需要完全在 JavaScript 回调中实现 data_generator 函数以及角度计算。不清楚您想用您的代码实现什么,但这里有一些示例 JS 回调实现基于您的代码更改饼图角度(使用 Bokeh v2.1.1 测试):

from re import I
import pandas as pd
from bokeh.plotting import show, figure, output_notebook
from bokeh.models import CustomJS, Slider, Column
from bokeh.palettes import Plasma
from bokeh.transform import cumsum
from math import pi
output_notebook()

vals = [20, 100, 50]
names = ["Agnostic", "Competetive", "Loyalist"]

def data_generator(names, vals, f):
    data = pd.DataFrame([[name, val * f] for name, val in zip(names, vals)], columns=['name', 'value'])
    return data

f_start = 30
data = data_generator(names, vals, f_start)

data['angle'] = data['value']/data['value'].sum() * 2*pi # print(data.to_string())
data['color'] = Plasma[len(data)]

p = figure(plot_height=350, title="Loyalty Breakout", toolbar_location=None,
           tools="hover", tooltips="@segment: @value", x_range=(-0.5, 1.0))

wedge = p.wedge(x=0, y=1, radius=0.4,
        start_angle=cumsum('angle', include_zero=True), end_angle=cumsum('angle'),
        line_color="white", fill_color='color', legend_field='name', source=data)

update = CustomJS(args=dict(wedge=wedge, vals=vals, xr=p.x_range), code="""
    var f = cb_obj.value;
    //var scaled = Array.from(vals, (x) => x*f)
    var scaled = Array.from(vals, (x, i) => i==0?x*f/10:x)
    var sum = scaled.reduce((a, b) => a + b, 0)
    var angles =  Array.from(scaled, (x) => x/sum * 2 * Math.PI)

    wedge.data_source.data['angle'] = angles
    wedge.data_source.data['value'] = scaled
    wedge.data_source.change.emit()

    //xr.start = a;
    //xr.end = b;
""")

lookback_slider = Slider(start=0, end=180, value=f_start, step=1, title="Lookback (days)")
lookback_slider.js_on_change('value',update)

p.axis.axis_label=None
p.axis.visible=False
p.grid.grid_line_color = None

show(Column(lookback_slider, p))