Skip to content Skip to sidebar Skip to footer

How To Use A Slider Callback To Filter A Columndatasource In Bokeh Using Python 3?

I'm trying to use a slider with a callback in Bokeh using Python 3 to filter the rows of my ColumnDataSource objects (which originate from a DataFrame). More specifically, if a sl

Solution 1:

Here is a solution using CustomJSFilter and CDSView as suggest in the other answer by Alex. It does not directly use the data as supplied in the question, but is rather a general hint how this can be implemented:

from bokeh.layouts import column
from bokeh.models import CustomJS, ColumnDataSource, Slider, CustomJSFilter, CDSView
from bokeh.plotting import Figure, show
import numpy as np

# Create some data to display
x = np.arange(200)
y = np.random.random(size=200)

source = ColumnDataSource(data=dict(x=x, y=y))
plot = Figure(plot_width=400, plot_height=400)

# Create the slider that modifies the filtered indices# I am just creating one that shows 0 to 100% of the existing data rows
slider = Slider(start=0., end=1., value=1., step=.01, title="Percentage")

# This callback is crucial, otherwise the filter will not be triggered when the slider changes
callback = CustomJS(args=dict(source=source), code="""
    source.change.emit();
""")
slider.js_on_change('value', callback)

# Define the custom filter to return the indices from 0 to the desired percentage of total data rows. You could also compare against values in source.data
js_filter = CustomJSFilter(args=dict(slider=slider, source=source), code=f"""
desiredElementCount = slider.value * 200;
return [...Array(desiredElementCount).keys()];
""")

# Use the filter in a view
view = CDSView(source=source, filters=[js_filter])
plot.line('x', 'y', source=source, line_width=3, line_alpha=0.6, view=view)

layout = column(slider, plot)

show(layout)

I hope this helps anyone who stumbles upon this in the future! Tested in bokeh 1.0.2

Solution 2:

A quick solution with minimal change to your code would be:

def slider_callback(attr, old, new):
    N = new  # this works also with slider.value but new is more explicit
    new1 = ColumnDataSource(df.loc[(df.winner == 'Democratic') & (df.population >= N)])
    new2 = ColumnDataSource(df.loc[(df.winner == 'Republican') & (df.population >= N)])
    source1.data = new1.data
    source2.data = new2.data

When updating data sources, you should replace the data, not the whole object. Here I still create new ColumnDataSource as shortcut. A more direct way (but more verbose too) would be to create the dictionary from the filtered df's columns:

    new1 = {
        'winner': filtered_df.winner.values,
        'pct_d': filtered_df.pct_d.values,
        ...
    }
    new2 = {...}
    source1.data = new1
    source2.data = new2

Note that there's another solution which would make the callback local (not server based) by using a CDSView with a CustomJSFilter. You can also write the other callback with a CDSView as well make the plot completely server-independent.

Post a Comment for "How To Use A Slider Callback To Filter A Columndatasource In Bokeh Using Python 3?"