我正在尝试在带有 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 更新饼图?
答案 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))