问题
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.
- I cannot seem to access the value of the multiselect object after I click it.
- I cannot seem to push the change to the notebook after receiving the change.
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)
回答1:
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
withpush_notebook
. Typically this involved theinteract
function to automatically generate a simple UI with callbacks that usepush_notebook
to update the plots, etc. based on the widget values. Just to be clear, this approach usesipywidgets
, 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 functionkernel.execute
to propagate values back to the kernel.
来源:https://stackoverflow.com/questions/50826750/how-to-link-a-multiselect-widget-to-a-datatable-using-bokeh-in-a-jupyter-noteboo