0

I'm trying to create slider that as you drag the slider, the portion of the graph that is shown is only what is on the slider. For example, if you look at my graph below, if the slider was set to 1990, you would only see the lines from 1990 to 2016. I found a working example with plotly but I wanted to see if it could be done with Bokeh. enter image description here

This is my code so far:

p = figure(width = 900, height = 450)
p.xaxis.axis_label = 'Year'
p.yaxis.axis_label = 'Aggregated Number of Degrees in Education'

source = ColumnDataSource(df)
fill_source = ColumnDataSource(data=dict(x=[],y=[]))

# Create objects for each line that will be plotted
stem = p.line('year', 'stem', line_color='#8dd3c7', line_width=3, source=source)
stem = p.circle('year', 'stem', line_color='#8dd3c7', line_width=3, source=source)
sped = p.line('year', 'sped', line_color='#fdb462', line_width=3, source=source)
elem = p.line('year', 'elem', line_color='#bebada', line_width=3, source=source)
elem = p.square('year', 'elem', line_color='#bebada', line_width=3, source=source)
other = p.line('year', 'other', line_color='#fb8072', line_width=4, source=source)
aggtotal = p.line('year', 'aggtotal', line_dash=[4,4,], line_color='#80b1d3', line_width=3, source=source)

yaxis = p.select(dict(type=Axis, layout="left"))[0]
yaxis.formatter.use_scientific = False    
legend = Legend(items=[("STEM", [stem])
                       ,("SPED" , [sped])
                       ,("Elementary", [elem])
                       ,("Other", [other])
                       ,("Total Education Graduates", [aggtotal])], location=(0, 0))
p.add_tools(HoverTool(tooltips=[("Date", "@year")]))
p.add_layout(legend, 'right')    

callback_test = CustomJS(args=dict(source=source,fill_source=fill_source), code="""
var data = source.data;
var fill_data = fill_source.data;
var s_val = cb_obj.value;
fill_data['x']=[];
fill_data['y']=[];
for (i = 0; i < s_val; i++) {
    fill_data['y'][i].push(data['y'][i]);
    fill_data['x'][i].push(data['x'][i]);          
    }
fill_source.trigger('change');
""")

sped_slider = Slider(start=1984, end= 2016, value=1, step=1,title="Year",callback=callback_test)
callback_test.args["sped"] = sped_slider
layout = row(p,widgetbox(sped_slider))

This renders a slider but it doesn't do anything and I'm not sure where to go from here.

1 Answer 1

2

There are some issues with your callback code. For example:

  • you loop i from 0 to s_val (which may be 1990) which is not consistent with the length of your arrays.
  • your glyphs references columns 'stem', etc... but the fill_source has columns 'x' and 'y'
  • your glyphs reference source as a source but you change and trigger event on fill_source.

All that could probably be fixed but there's a much easier way, adjust the range in the callback. E.g. replace your callback by this:

x_range = p.x_range
callback_test = CustomJS(args=dict(x_range=x_range), code="""
    var start = cb_obj.value;
    x_range.start = start;
    x_range.change.emit();
""")

Note the change to the event trigger. Your version would work but I think it's going to be deprecated.

Also:

  • this line callback_test.args["sped"] = sped_slider is not necessary
  • you could add toolbar_location='above' in figure(...) to avoid rendering conflict with the legend
  • you're still going to have a layout problem between the slider and the legend which can be fixed in different ways (slider under or put the slider and the legend in a column before adding to the right of the plot, etc...)

Not the answer you're looking for? Browse other questions tagged or ask your own question.