简体   繁体   中英

Multiple filters in Python Bokeh & Javascript

this builds on my previous question ( Filter data with Javascript callback in Python's Bokeh ) that was successfully answered and takes it one step further.

When I try to filter my plot with multiple filters in Python's Bokeh, the function returns an empty plot.

I can't find any conceptual differences to the working solution with one filter so would appreciate any hint.

Below is a reproducible example:

# Packages
import bokeh.events as bev
import bokeh.layouts as bla
import bokeh.models as bmo
import numpy as np
import pandas as pd
from bokeh.plotting import figure, output_file, show

# Data
data = pd.DataFrame(
    data=np.array(
        [
            [1, 1, 'a', 'c', 0.5],
            [2, 2, 'a', 'c', 0.5],
            [3, 3, 'a', 'd', 0.75],
            [4, 4, 'b', 'd', 1],
            [5, 5, 'b', 'd', 2]
        ]
    ),
    columns=['x', 'y', 'category 1', 'category 2', 'other information']
)

# Setup
output_file('dashboard.html')

source = bmo.ColumnDataSource(data)

# Define dropdown options
dropdown_options1 = [
                       ('All', 'All'), None
                   ] + [(cat, cat)
                       for i, cat in enumerate(sorted(data['category 1'].unique()), 2)
                   ]
dropdown_options2 = [
                       ('All', 'All'), None
                   ] + [(cat, cat)
                       for i, cat in enumerate(sorted(data['category 2'].unique()), 2)
                   ]

# Generate dropdown widget
dropdown1 = bmo.Dropdown(label='Category1', button_type='default', menu=dropdown_options1)
dropdown2 = bmo.Dropdown(label='Category2', button_type='default', menu=dropdown_options2)

filtered_data = bmo.ColumnDataSource(data)
# Callback
callback = bmo.CustomJS(
    args = dict(unfiltered_data = source, filtered_data = filtered_data, dropdown1 = dropdown1 , dropdown2 = dropdown2 ),
    code = """

var data = unfiltered_data.data;
var ind = dropdown1.item;
var ctr = dropdown2.item;

function generateNewDataObject(oldDataObject){
    
    var newDataObject = {}
    
    for (var key of Object.keys(oldDataObject)){
            
        newDataObject[key] = [];
        
    }
    
    return newDataObject

}


function addRowToAccumulator(accumulator, dataObject, index) {
    
    for (var key of Object.keys(dataObject)){
            
        accumulator[key][index] = dataObject[key][index];
        
    }
    
    return accumulator;
}


if (ind === 'All' && ctr === 'All'){
                
    data = unfiltered_data.data;
                
} else if (ctr === 'All') {
            
    var new_data =  generateNewDataObject(data);
    
    for (var i = 0; i <= unfiltered_data.data['category 1'].length; i++){
            
        if (unfiltered_data.data['category 1'][i] == ind) {
                
            new_data = addRowToAccumulator(new_data, unfiltered_data.data, i);
            
        }
    }
    
    data = new_data;
    
} else if (ind === 'All'){
    
    var new_data =  generateNewDataObject(data);
    
    for (var i = 0; i <= unfiltered_data.data['category 2'].length; i++){
            
        if (unfiltered_data.data['category 2'][i] == ctr) {
                
            new_data = addRowToAccumulator(new_data, unfiltered_data.data, i);
            
        }
    }
    
    data = new_data;
        
} else {
        
    var new_data =  generateNewDataObject(data);
    
    for (var i = 0; i <= unfiltered_data.data['category 1'].length; i++){
            
        if (unfiltered_data.data['category 2'][i] == ctr && unfiltered_data.data['category 1'][i] == ind) {
                
            new_data = addRowToAccumulator(new_data, unfiltered_data.data, i);
            
        }
    }
    
    data = new_data;            
                        
}

filtered_data.data = data;
filtered_data.change.emit();
"""
)


# Link actions
dropdown1.js_on_event(bev.MenuItemClick, callback)
dropdown2.js_on_event(bev.MenuItemClick, callback)

# Plot
p1 = figure(plot_width=800, plot_height=530, title=None)

p1.scatter(x='x', y='y', source=filtered_data)

show(bla.column(bla.row(dropdown1, dropdown2), p1))

I tried to access the dropdowns' values with different approaches ( .item , .value and .text ) but without any success.

Thank you for any support, Oliver

Dropdown is a click event, so the information is only in the event, not on any property of the widget itself (if you want that, maybe a Select widget would be a better choice than Dropdown ). There is an example in the docs that shows how to access the item in the event:

dropdown = Dropdown(label="Dropdown button", menu=menu)
dropdown.js_on_event(
    "menu_item_click", 
    CustomJS(code="console.log('dropdown: ' + this.item")
)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM