Search code examples
pythonpython-3.xbokehbokehjs

Applying .filter() to ColumnDataSource in Bokeh JS Callback


The problem: I am not able to apply a filter function to columndatasource, and even after applying it's giving my entire full array. My lack of familiarity with JS is making things worse.

So I've been trying to reproduce the results from here:

https://discourse.bokeh.org/t/possible-to-use-customjs-callback-from-a-button-to-animate-a-slider/3985

But using my own data source.

Here is the code for JS Callback:

# Create slider callback
SliderCallback = CustomJS(args = dict(sliceCDS=sliceCDS, fullCDS=fullCDS, indexCDS=indexCDS), code = """
    const new_value = cb_obj.value;

    // Take the 'Slice' column from the full data
    const slice_col = fullCDS.data['Slice'];

    // Select only the values equal to the new slice number
    const mask = slice_col.map((item) => item==new_value);

    sliceCDS.data['x'] = fullCDS.data['x'].filter(item => mask);
    sliceCDS.data['y'] = fullCDS.data['y'].filter(item => mask);
    sliceCDS.data['label'] = fullCDS.data['label'].filter(item => mask);

    //console.log('Here is the data');
    console.log(sliceCDS.data['x']);
    console.log(sliceCDS.data['y']);
    console.log(sliceCDS.data['label']);


    // Update the sliceCDS
    sliceCDS.change.emit();
    """)

# Set up slider
slider = bokeh.models.Slider(title="Slice view number: ",start=0, end=49,value=0,step=1)
slider.js_on_change('value', SliderCallback)

Here is sliceCDS(Input to JS callback):

{'x': 0   -0.001215
0    0.001454
0   -0.000191
0   -0.000377
0   -0.000008
       ...   
0    0.001993
0    0.002045
0    0.002220
0   -0.003160
0   -0.000088
Name: x, Length: 1797, dtype: float64, 'y': 0    0.000745
0    0.000171
0   -0.000004
0    0.000268
0    0.000535
       ...   
0   -0.000417
0    0.002719
0   -0.000269
0    0.000766
0    0.000250
Name: y, Length: 1797, dtype: float64, 'label': 0    0
0    0
0    0
0    0
0    0
    ..
0    9
0    9
0    9
0    9
0    9
Name: label, Length: 1797, dtype: int64, 'Slice': 0    0
0    0
0    0
0    0
0    0
    ..
0    0
0    0
0    0
0    0
0    0
Name: Slice, Length: 1797, dtype: int64}

Here is fullCDS(Input to JS callback):

{'x': 0     -0.001215
0      0.001454
0     -0.000191
0     -0.000377
0     -0.000008
        ...    
49   -12.208837
49   -11.620906
49   -16.709465
49   -13.481855
49   -12.067336
Name: x, Length: 89850, dtype: float64, 'y': 0      0.000745
0      0.000171
0     -0.000004
0      0.000268
0      0.000535
        ...    
49    28.264780
49    27.768742
49    27.019316
49    27.537040
49    24.889742
Name: y, Length: 89850, dtype: float64, 'label': 0     0
0     0
0     0
0     0
0     0
     ..
49    9
49    9
49    9
49    9
49    9
Name: label, Length: 89850, dtype: int64, 'Slice': 0      0
0      0
0      0
0      0
0      0
      ..
49    49
49    49
49    49
49    49
49    49
Name: Slice, Length: 89850, dtype: int64}

With the above code I am not able to update my sliceCDS. It still contains all 89850 rows, when it should contains only 1797 after applying .filter(). EDIT: on suggestion, I also tried the following in the function:

sliceCDS.data['x'] = fullCDS.data['x'].filter((item,idx) => mask[idx]);

and it gives me following error:

VM499:17 Uncaught TypeError: Cannot read property 'filter' of undefined

and tried with this also:

sliceCDS.data['x'] = fullCDS.data['x'].filter((item,0) => mask[0]);

which gave me this error:

VM505:31 Uncaught SyntaxError: Invalid destructuring assignment target

But if I try the method from the link given above, with names of the columns in my columndatasources as Line1 for x, Line2 for y and Line3 for label:

// Update the data for sliceCDS with a slice of data from fullCDS
    for(i=1; i<3; i++){
        sliceCDS.data['Line' + i.toString()] = fullCDS.data['Line' + i.toString()].filter((item,i) => mask[i]);
    }

The above code works just fine.

But why is the code for case mentioned in the starting not working? I am sure there's something wrong with the way I am using .filter().


Solution

  • You have this expression in there.

    fullCDS.data['x'].filter(item => mask);
    

    The item => mask always returns the mask, regardless of what item is. In JavaScript, an array is always truth-y, no matter the contents.

    The second argument to the filter function is the index of the item. Try using the (item, idx) => mask[idx] lambda instead.