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.