0

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?

1 Answer 1

1

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))
Sign up to request clarification or add additional context in comments.

1 Comment

I gave this a check because it does work. However, I found another solution that works using only Python with ipywidgets. Github: github.com/groundhogday321/python-bokeh/blob/master/… YouTube video: youtube.com/watch?v=o4TB6LTPDaY

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.