I am trying to create a pie chart in a jupyter notebook with Bokeh that can be updated with a slider. I have a custom function that creates data
from a pre-existing dataframe. I would like the slider to manipulate input f
to that function, such that data
is different when displayed in the pie graph. Here is an example:
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))
This creates the pie chart and slider, but the slider does not work. I found this: Using bokeh to plot interactive pie chart in Jupyter/Python but unfortunately, CustomJS.from_py_func(update)
won't work because from_py_func
has been deprecated in the newest version of Bokeh. Can anyone help me update a pie chart with Bokeh?
You need to implement your data_generator
function as well as the angle calculation entirely in your JavaScript callback. It is not clear what you are trying to achieve with your code but here is some example JS callback implementation based on your code that changes the pie angle (tested with 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))