Reputation: 2981
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)
Upvotes: 3
Views: 6095
Reputation: 2137
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.
Upvotes: 2