问题
I have a bunch of data plotted in a scatter plot using a ColumnDataSource and one column is the X coordinates another the Y coordinates. There's a third column with a patient ID that may have duplicates. I'd like to create a Tap derived tool that would select all other x,y coordinates that share the same value in the patient ID column.
'x' 'y' 'ID'
1 2 'p1'
2 3 'p1'
2 5 'p2'
0 1 'p2'
So basically if I click in my bokeh scatter plot the coordinate (1,2) I'd get points at (1,2) and (2,3) selected with all others unselected much like how you find the lasso and boxselect tools behaving.
回答1:
Here is an example solution using CustomJS with bokeh 0.12.6. Essentially when the user selects a glyph, you know which row that corresponds to. Look up the associated id value and then add all other data points with the same id to the selected attribute of a ColumnDataSource.
You can also create an equivalent call back using python only. However that will require the use of a bokeh server. The code will also be quite similar.
from bokeh.plotting import figure, output_file, show from bokeh.models import ColumnDataSource, CustomJS
# output to static HTML file
output_file("line.html")
p = figure(plot_width=400, plot_height=400,tools=["tap"])
x =[1,2,2,0]
y = [2,3,5,1]
n_id = ['p1','p1','p2','p2']
data = {'x':x,'y':y,'id':n_id}
source = ColumnDataSource(data)
# source callback
code = """
var data = source.data,
selected = source.selected['1d']['indices'],
select_inds = [selected[0]];
if(selected.length == 1){
// only consider case where one glyph is selected by user
selected_id = data['id'][selected[0]]
for (var i = 0; i < data['x'].length; ++i){
if(data['id'][i] == selected_id){
// add all points to selected if their ids coincide with original
// glyph that was clicked.
select_inds.push(i)
}
}
}
source.selected['1d']['indices'] = select_inds
source.change.emit();
"""
callback = CustomJS(args={'source':source},code=code)
source.callback=callback
p.circle('x','y',source=source, size=20, color="navy", alpha=0.5)
# show the results
show(p)
In recent versions of bokeh the notation for accessing the selected indices has been simplified somewhat. For compatibility with bokeh 0.13.0, the JS callback could become:
code = """
var data = source.data,
selected = source.selected.indices,
select_inds = [];
if(selected.length == 1){
// only consider case where one glyph is selected by user
selected_id = data['id'][selected[0]]
for (var i = 0; i < data['x'].length; ++i){
if(data['id'][i] == selected_id){
// add all points to selected if their ids coincide with original
// glyph that was clicked.
select_inds.push(i)
}
}
}
source.selected.indices = select_inds
source.change.emit();
"""
来源:https://stackoverflow.com/questions/44961192/how-to-create-a-bokeh-tap-tool-to-select-all-points-of-the-columndatasource-shar