selection callback on Bokeh server

Using Bokeh server (v.11), I currently have an app workflow that looks like this:

  1. Plot all data points in a set (about 80 points total)

  2. Filter that original data set with a Python callback along the lines of plot_source.data = ColumnDataSource(new_source).data, where new_source is a Pandas data frame. This narrows the plot to about 4 points total.

  3. Select a point on the (filtered) plot.

  4. Get a unique identifier for the selected point, along the lines of unique_id = plot_source.data['unique_identifier'][plot_source.selected['1d']['indices'][0]]

  5. Remove the filter, effectively reversing step 2.

  6. Change plot_source.selected to reflect the new index the previously-extracted unique identifier: plot_source.selected['1d']['indices'] = [plot_source.data['unique_identifier'].index(unique_id)]

All of this works fine on the python side - in my app (too complicated and dependent on internal resources to share here), I selected a point from the filtered set so that, say, plot_source.selected['1d']['indices'] == [3], and the workflow above determines that in the unfiltered set, that same unique identifier is places in the source so plot_source.selected['1d']['indices'] == [72]. However, in the browser, that change doesn’t seem to take effect: while the python side shows the selected index is 72, the browser thinks it is still 3, which means un-filtering the data effectively changes which point is selected.

I see from examples on the site that, when doing a JavaScript callback, it’s necessary to call source.trigger('change') for the changes to take effect. Is there something comparable that I need to do when running a Python callback?

I wanted to check quickly here, to see if this was simply a matter of my ignorance, before working up a toy example to illustrate the issue. Thank you in advance for any help you can offer.

Here’s an example showing how this works using the JS callbacks! I don’t know how updating the selection from the python side works.

from bokeh.plotting import Figure, output_file, show

from bokeh.models import CustomJS

from bokeh.models.sources import ColumnDataSource

from bokeh.io import vform

from bokeh.models.widgets import DataTable, TableColumn, Toggle

from random import randint

import pandas as pd

output_file(“data_table_subset_example.html”)

data = dict(

    x=[randint(0, 100) for i in range(10)],

    y=[randint(0, 100) for i in range(10)],

    z=['some other data'] * 10

)

df = pd.DataFrame(data)

#filtering dataframes with pandas keeps the index numbers consistent

filtered_df = df[df.x < 80]

#Creating CDSs from these dataframes gives you a column with indexes

source1 = ColumnDataSource(df)

source2 = ColumnDataSource(filtered_df)

fig1 = Figure(plot_width=200, plot_height=200)

fig1.circle(x=‘x’, y=‘y’, source=source1)

columns = [

    TableColumn(field="x", title="X"),

    TableColumn(field="z", title="Text"),

]

data_table = DataTable(source=source2, columns=columns, width=400, height=280)

button = Toggle(label=“Select”)

button.callback = CustomJS(args=dict(source1=source1, source2=source2), code="""

    var inds_in_source2 = source2.get('selected')['1d'].indices;

    var d = source2.get('data');

    var inds = []

    

    if (inds_in_source2.length == 0) { return; }

    for (i = 0; i < inds_in_source2.length; i++) {

        inds.push(d['index'][i])

    }

    source1.get('selected')['1d'].indices = inds

    source1.trigger('change');

""")

show(vform(fig1, data_table, button))

···

On Monday, February 8, 2016 at 1:20:34 PM UTC-8, [email protected] wrote:

Using Bokeh server (v.11), I currently have an app workflow that looks like this:

  1. Plot all data points in a set (about 80 points total)
  1. Filter that original data set with a Python callback along the lines of plot_source.data = ColumnDataSource(new_source).data, where new_source is a Pandas data frame. This narrows the plot to about 4 points total.
  1. Select a point on the (filtered) plot.
  1. Get a unique identifier for the selected point, along the lines of unique_id = plot_source.data['unique_identifier'][plot_source.selected['1d']['indices'][0]]
  1. Remove the filter, effectively reversing step 2.
  1. Change plot_source.selected to reflect the new index the previously-extracted unique identifier: plot_source.selected['1d']['indices'] = [plot_source.data['unique_identifier'].index(unique_id)]

All of this works fine on the python side - in my app (too complicated and dependent on internal resources to share here), I selected a point from the filtered set so that, say, plot_source.selected['1d']['indices'] == [3], and the workflow above determines that in the unfiltered set, that same unique identifier is places in the source so plot_source.selected['1d']['indices'] == [72]. However, in the browser, that change doesn’t seem to take effect: while the python side shows the selected index is 72, the browser thinks it is still 3, which means un-filtering the data effectively changes which point is selected.

I see from examples on the site that, when doing a JavaScript callback, it’s necessary to call source.trigger('change') for the changes to take effect. Is there something comparable that I need to do when running a Python callback?

I wanted to check quickly here, to see if this was simply a matter of my ignorance, before working up a toy example to illustrate the issue. Thank you in advance for any help you can offer.