3
votes

Using the example here

http://docs.bokeh.org/en/latest/docs/gallery/stacked_bar_chart.html

along with the interaction example here:

http://docs.bokeh.org/en/latest/docs/user_guide/interaction.html#userguide-interaction

It seems like it should be pretty straightforward to have a slider change the visualization of the stacked barchart. However, the problem is the Bar object from the bokeh.charts library does not expose where the data that is rendered in the bargraph is sourced. As far as I can tell, this is because the charts module does not maintain this data.

Is it possible to have an interaction as described above using the Bar object. I'm stuck as to where to source the data so the barchart can be updated when the slider moves. I really don't want to have to rebuild the entire barchart from primitives explicitly every time the slider moves.

Any help appreciated. My hail-mary attempt is isted below:

from collections import OrderedDict
import pandas as pd
from bokeh.charts import Bar, output_file, show
from bokeh.sampledata.olympics2014 import data
from bokeh.io import vform
from bokeh.models import Callback, ColumnDataSource, Slider
from bokeh.plotting import figure

output_file("callback_bar_graph.html")

df = pd.io.json.json_normalize(data['data'])

# filter by countries with at least one medal and sort
df = df[df['medals.total'] > 0]
df = df.sort("medals.total", ascending=False)

# get the countries and we group the data by medal type
countries = df.abbr.values.tolist()
gold = df['medals.gold'].astype(float).values
silver = df['medals.silver'].astype(float).values
bronze = df['medals.bronze'].astype(float).values

# build a dict containing the grouped data
medals = OrderedDict(bronze=bronze, silver=silver, gold=gold)

# any of the following commented are also alid Bar inputs
medals = pd.DataFrame(medals)

source = ColumnDataSource(data=dict(medals=medals, countries=countries))

bar = Bar(medals, countries, title="Stacked bars", stacked=True)

callback = Callback(args=dict(source=source), code="""
    var data = source.get('data');
    var f = cb_obj.get('value')
    medals = data['medals']
    countries = data['countries']
    for (i = 0; i < medals.bronze.length; i++) {
        medals.bronze[i] = 2*medals.bronze[i]
    }
    source.trigger('change');
""")

slider = Slider(start=-2, end=2, value=1, step=.1,
                title="value", callback=callback)

layout = vform(slider, bar)

show(layout)
1
In so far as working directly with a BarChart object, I've found no way to modify the plot object in a(n obvious) way that would facilitate what you're looking for. The reason for this is that the BarChart stores all of it's chart data in its renderers. This wouldn't be a problem if you could pass another BarChart's renderers to the callback, but callbacks can only receive PlotObjects as arguments, and thus your list of renderers is immediately disqualified...StevieP
Also, this might not help you but I was able to hack together an interactive stacked barchart. Check this out.StevieP

1 Answers

2
votes

The answer is callbacks!

source: http://docs.bokeh.org/en/latest/docs/user_guide/interaction.html#callbacks-for-widgets

Essentially, you'll want to create a ColumnDataSource object containing your bar chart data. Then you'll create a Callback object thats the ColumnDataSource instance as an argument and JS code that gets the data from a slider interaction and updates the data instance/triggers the change.

If you can't get it to work, post the code you have and I'll try to help.