Reputation: 13
I'm attempting to connect a datatable with a multiselect widget in bokeh. I've searched around and gathered that I need to develop a function to update the data source for the data table, but I seem to have two problems.
Here's an example of my code:
import pandas as pd
from bokeh.io import push_notebook
from bokeh.plotting import show, output_notebook
from bokeh.layouts import row
from bokeh.models.widgets import MultiSelect, DataTable, TableColumn
from bokeh.models import ColumnDataSource
output_notebook()
df=pd.DataFrame({'year':[2000,2001,2000,2001,2000,2001,2000,2001],
'color':['red','blue','green','red','blue','green','red','blue'],
'value':[ 0,1,2,3,4,5,6,7]})
columns=[TableColumn(field=x, title=x) for x in df.columns]
source=ColumnDataSource(df)
data_table=DataTable(source=source,columns=columns)
years=[2000,2001,2000,2001,2000,2001,2000,2001]
## MultiSelect won't let me store an integer value, so I convert them to strings
multi=MultiSelect(title="Select a Year", value=['2000','2001'],options=[str(y) for y in set(years)])
def update(attr,old, new):
yr=multi.value
yr_vals=[int(y) for y in yr]
new_data=df[df.year.isin(yr_vals)]
source.data=new_data
push_notebook(handle=t)
multi.on_change('value',update)
t=show(row(multi,data_table),notebook_handle=True)
Upvotes: 1
Views: 1685
Reputation: 34568
push_notebook
is uni-directional. That is, you can only push changes from the IPython kernel, to the JavaScript front end. No changes from the UI are propagated back to the running Python kernel. In other words, on_change
is not useful (without more work, see below) If you want that kind of interaction, there are a few options:
Use ipywidgets
with push_notebook
. Typically this involved the interact
function to automatically generate a simple UI with callbacks that use push_notebook
to update the plots, etc. based on the widget values. Just to be clear, this approach uses ipywidgets
, which are not Bokeh built-in widgets. You can see a full example notebook here:
https://github.com/bokeh/bokeh/blob/master/examples/howto/notebook_comms/Jupyter%20Interactors.ipynb
Embed a Bokeh server application. The Bokeh server is what makes it possible for on_change
callbacks on Bokeh widgets to function. Typically this involves making a function that defines the app (by specifying how a new document is created):
def modify_doc(doc):
df = sea_surface_temperature.copy()
source = ColumnDataSource(data=df)
plot = figure(x_axis_type='datetime', y_range=(0, 25))
plot.line('time', 'temperature', source=source)
def callback(attr, old, new):
if new == 0:
data = df
else:
data = df.rolling('{0}D'.format(new)).mean()
source.data = ColumnDataSource(data=data).data
slider = Slider(start=0, end=30, value=0, step=1, title="Smoothing by N Days")
slider.on_change('value', callback)
doc.add_root(column(slider, plot))
Then calling show
on that function:
show(modify_doc)
A full example notebook is here:
https://github.com/bokeh/bokeh/blob/master/examples/howto/server_embed/notebook_embed.ipynb
(Hacky option) some people have combined CustomJS
callbacks with Jupyers JS function kernel.execute
to propagate values back to the kernel.
Upvotes: 4