问题
I am plotting several patches that are grouped by a category "group" in the data source. What would I like to achieve is the following: By clicking on one patch, not only the patch itself but all patches of the same group should be highlighted as selected.
I found that ColumnDataSource has an attribute selected. However, manipulating this attribute in the callback function does not have the desired effect.
import os
from bokeh.models import ColumnDataSource, Patches
from bokeh.plotting import figure
from bokeh.layouts import row
from bokeh.io import output_file, curdoc
import pandas as pd
x = [[1,2,4], [3,5,6], [7,9,7], [5,7,6]]
y = [[4,2,1], [6,5,8], [3,9,6], [2,2,1]]
group = ['A', 'A', 'B', 'B']
id = [0,1,2,3]
df = pd.DataFrame(data=dict(x=x, y=y, group=group, id=id))
source = ColumnDataSource(df)
p = figure(tools="tap")
renderer = p.patches('x', 'y', source=source)
# Event handler
def my_tap_handler(attr,old,new):
global source
group_name = source.data['group'][new['1d']['indices'][0]]
group_indices = df['id'][df['group'] == group_name]
source.selected.indices = list(group_indices)
print("source.selected.indices", source.selected.indices)
selected_patches = Patches(fill_color="#a6cee3")
renderer.selection_glyph = selected_patches
# Event
renderer.data_source.on_change("selected", my_tap_handler)
#######################################
# Set up layouts and add to document
curdoc().add_root(row(p, width=800))
回答1:
You can do the selection in Javascript, but if you really want to do this in Python, here is an example:
import os
from bokeh.models import ColumnDataSource, Patches, CustomJS
from bokeh.plotting import figure
from bokeh.layouts import row
from bokeh.io import output_file, curdoc
import pandas as pd
def app(doc):
x = [[1,2,4], [3,5,6], [7,9,7], [5,7,6]]
y = [[4,2,1], [6,5,8], [3,9,6], [2,2,1]]
group = ['A', 'A', 'B', 'B']
id = [0,1,2,3]
df = pd.DataFrame(data=dict(x=x, y=y, group=group, id=id))
source = ColumnDataSource(df)
p = figure(tools="tap")
renderer = p.patches('x', 'y', source=source)
def my_tap_handler(attr,old,new):
indices = source.selected.indices
if len(indices) == 1:
group = source.data["group"][indices[0]]
new_indices = [i for i, g in enumerate(source.data["group"]) if g == group]
if new_indices != indices:
source.selected = Selection(indices=new_indices)
selected_patches = Patches(fill_color="#a6cee3")
renderer.selection_glyph = selected_patches
source.on_change("selected", my_tap_handler)
doc.add_root(row(p, width=800))
show(app)
来源:https://stackoverflow.com/questions/50492995/bokeh-server-how-to-manipulate-a-selection-in-a-callback-function