1

I have a simple ColumnDataSource of multiple columns, each column representing a different day of a simulation and each row representing the number of entities with status a, b, c,.. etc. I'd like to be able to scrub through the days (columns) with a slider.

I've taken a look at 1, 2, and the Bokeh docs for information but I haven't been able to successfully get it working. I have the following code (minimal):

from bokeh.plotting import figure
from bokeh.layouts import column, widgetbox
from bokeh.models import CustomJS, Slider, ColumnDataSource, ranges
from bokeh.io import output_file, show

output_file("barplot.html")
sim_time=4

data = {'x': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
        '0': [2860.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        '1': [2860.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        '2': [0.0, 2860.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        '3': [0.0, 2860.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]}

source = ColumnDataSource(data)

barplot = figure(plot_width=800, plot_height=600, tools='pan', x_axis_label='Status', x_range=source.data['x'], y_range=ranges.Range1d(start=0, end=3000))
barplot.vbar(source=source, x='x', top='0', width=0.6)

callback = CustomJS(args={'source':source}, code="""
    console.log(' changed selected time', cb_obj.value);
    var data = source.data;
    data['0'] = data[cb_obj.value];
    source.change.emit()
""")

time_slider = Slider(start=0, end=sim_time-1, value=1, step=1, callback=callback)
callback.args["time"] = time_slider

show(column(barplot, time_slider))

i.e. I'm not able to scrub through the columns using a slider.

What am I doing wrong?

Cheers

okurniawan
  • 13
  • 3

1 Answers1

1

Try this:

data = {'x': ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'],
        'y': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        '0': [0.0, 0.0, 0.0, 0.0, 500.0, 0.0, 0.0, 0.0],
        '1': [0.0, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        '2': [0.0, 0.0, 1000.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        '3': [0.0, 0.0, 0.0, 1000.0, 0.0, 0.0, 0.0, 0.0]}

source = ColumnDataSource(data)

barplot = figure(plot_width=800, plot_height=600, tools='pan',
                 x_axis_label='Status', x_range=source.data['x'],
                 y_range=ranges.Range1d(start=0, end=3000))
barplot.vbar(source=source, x='x', top='y', width=0.6)

callback = CustomJS(args=dict(source=source), code="""
    console.log(' changed selected time', cb_obj.value);
    var data = source.data;
    data['y'] = data[cb_obj.value];
    source.change.emit()
""")

time_slider = Slider(start=0, end=sim_time-1, value=0, step=1, callback=callback)
callback.args["time"] = time_slider    
show(column(barplot, time_slider))

There are two issues with your code. Your bars for 0 1 and 2 3 are identical. You won't see any change between choosing 2 and 3. Also, you use the 0 column for storing data. But then you overwrite the data in 0 in your JS code. Thus, the data gets lost. Consequently, switching back to 0 on the slider won't have an effect. This is why I added a new (empty) column y to your data. This is just a placeholder used for plotting holding the currently selected data.

mc51
  • 1,883
  • 14
  • 28
  • I appreciate the quick reply! I applied all your suggestions but the scrubbing is still not working. Upon looking at the browser console, it's telling me that source.change.emit() is throwing TypeErrors of "undefined is not an object". Any thoughts? – okurniawan Sep 26 '17 at 00:36
  • Solved the problem-- this functionality was broken on bokeh 0.12.5 but is fully functional on 0.12.7. – okurniawan Sep 26 '17 at 01:48