散景链接/基于列而不是行索引/使用选择工具的索引进行刷涂

时间:2019-03-18 16:50:17

标签: python plot bokeh

与以下问题有关的问题: Bokeh linking/ brushing based on column instead of row indices / index

我想使用选择工具进行多次选择,而不是“轻按”。为此,我使用的是customjs-for-selections所示的示例。不幸的是,我似乎陷入了使用source.selected.indices='new selection'的变化循环中。 关于如何在不触发js_on_change的情况下更新选择的任何建议?

from bokeh.plotting import figure, show
from bokeh.layouts import gridplot
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CustomJS
import pandas as pd
output_notebook()

data = {'person': [1, 1, 1, 2, 2, 3, 3, 3, 3], 
        'activities':['a', 'b', 'c', 'a', 'b', 'a', 'b', 'c', 'd'], 
        'hours':[3, 4, 6, 2, 7, 5, 3, 2, 12], 
        'foodeaten':[12, 14, 34, 45, 67, 5, -1, 3, 5]}
df     = pd.DataFrame(data = data)
print(df)
source = ColumnDataSource(data = df)
views  = [(df.activities == l) for l in ['a', 'b', 'c', 'd']]
filtered_views = [CDSView(source = source, filters = [BooleanFilter(view.values.tolist())]) for view in views]
plot_options = dict(plot_width = 250, 
                    plot_height = 250, 
                    tools = "tap,pan,wheel_zoom,reset,save,box_select", 
                    tooltips = [("Person", "@person"), ("hours", "@hours")])
plots = [figure(title = 'activity {l}'.format(l = l), **plot_options) for l in ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd']]
for plot, view, name in zip(plots, 2 * filtered_views, 4 * ['hours'] + 4 * ['foodeaten']):
    plot.circle('person', y = name, size = 15, view = view, source = source)

callback = CustomJS(args = dict(source = source, plots = plots), code = """
const selected_index = source.selected.indices[0]

const person = source.data['person'][selected_index]
var all_selected = [];
debugger;
for (index in source.data.index){
    if (source.data['person'][index] == person)
    all_selected.push(index)
}
source.selected.indices = all_selected
""")

callback1 = CustomJS(args = dict(source = source, ), code = """
var selected_indices = source.selected.indices;
var data = source.data;
debugger;
console.log(selected_indices)
var all_selected = [];
var persons = [];

for (i in selected_indices){
    index = selected_indices[i];
    console.log(data['person'][index]);
    persons.push(data['person'][index]);
    }


for (i in data.index){
    index = data.index[i]
    for (j in persons){
        person = persons[j]
        if (data['person'][i] == person){
            all_selected.push(index);
            }
    }
    }
source.selected.indices=all_selected;
""")


source.selected.js_on_change('indices', callback1)
output_file('testSelect.html')

show(gridplot(children = [plot for plot in plots], ncols = 2))

2 个答案:

答案 0 :(得分:1)

当用BoxSelectTool替换TapTool(在Bokeh v1.0.4上测试)时,来自上一示例的回调仍然有效:

from bokeh.plotting import figure, show
from bokeh.layouts import gridplot
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CustomJS, BoxSelectTool
import pandas as pd
# output_notebook()
data = {'person': [1, 1, 1, 2, 2, 3, 3, 3, 3],
        'activities':['a', 'b', 'c', 'a', 'b', 'a', 'b', 'c', 'd'],
        'hours':[3, 4, 6, 2, 7, 5, 3, 2, 12],
        'foodeaten':[12, 14, 34, 45, 67, 5, -1, 3, 5]}
df = pd.DataFrame(data = data)
print(df)
source = ColumnDataSource(data = df)
views = [(df.activities == l) for l in ['a', 'b', 'c', 'd']]
filtered_views = [CDSView(source = source, filters = [BooleanFilter(view.values.tolist())]) for view in views]
plot_options = dict(plot_width = 250,
                    plot_height = 250,
                    tools = "box_select,tap,pan,wheel_zoom,reset,save",
                    tooltips = [("Person", "@person"), ("hours", "@hours")])
plots = [figure(title = 'activity {l}'.format(l = l), **plot_options) for l in ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd']]
for plot, view, name in zip(plots, 2 * filtered_views, 4 * ['hours'] + 4 * ['foodeaten']):
    plot.circle('person', y = name, size = 15, view = view, source = source)

callback = CustomJS(args = dict(source = source, plots = plots), code = """
const selected_index = source.selected.indices[0]
const person = source.data['person'][selected_index]
var all_selected = [];
//debugger;
for (index in source.data.index){
    if (source.data['person'][index] == person)
    all_selected.push(index); }
source.selected.indices = all_selected; """)

# output_file('testSelect.html')
for plot in plots:
    plot.select_one(BoxSelectTool).callback = callback
show(gridplot(children = [plot for plot in plots], ncols = 2))

答案 1 :(得分:0)

基于托尼的答案(使用BoxSelectTool进行单选),我想出了用于多选的代码。 (请注意,我在原始的callback1函数中做了一些错字)

from bokeh.plotting import figure, show
from bokeh.layouts import gridplot
from bokeh.models import ColumnDataSource, CDSView, BooleanFilter, CustomJS, BoxSelectTool
import pandas as pd
# output_notebook()
data = {'person': [1, 1, 1, 2, 2, 3, 3, 3, 3],
        'activities':['a', 'b', 'c', 'a', 'b', 'a', 'b', 'c', 'd'],
        'hours':[3, 4, 6, 2, 7, 5, 3, 2, 12],
        'foodeaten':[12, 14, 34, 45, 67, 5, -1, 3, 5]}
df = pd.DataFrame(data = data)
print(df)
source = ColumnDataSource(data = df)
views = [(df.activities == l) for l in ['a', 'b', 'c', 'd']]
filtered_views = [CDSView(source = source, filters = [BooleanFilter(view.values.tolist())]) for view in views]
plot_options = dict(plot_width = 250,
                    plot_height = 250,
                    tools = "box_select,tap,pan,wheel_zoom,reset,save",
                    tooltips = [("Person", "@person"), ("hours", "@hours")])
plots = [figure(title = 'activity {l}'.format(l = l), **plot_options) for l in ['a', 'b', 'c', 'd', 'a', 'b', 'c', 'd']]
for plot, view, name in zip(plots, 2 * filtered_views, 4 * ['hours'] + 4 * ['foodeaten']):
    plot.circle('person', y = name, size = 15, view = view, source = source)


callback1 = CustomJS(args = dict(source = source, ), code = """
var selected_indices = source.selected.indices;
var data = source.data;
debugger;
console.log(selected_indices)
var all_selected = [];
var persons = [];

for (i in selected_indices){
    index = selected_indices[i];
    console.log(data['person'][index]);
    persons.push(data['person'][index]);
    }


for (i in data.index){
    index = data.index[i]
    for (j in persons){
        person = persons[j]
        if (data['person'][i] == person){
            all_selected.push(index);
            }
    }
    }
source.selected.indices=all_selected;
""")

# output_file('testSelect.html')
for plot in plots:
    plot.select(BoxSelectTool).callback = callback1
show(gridplot(children = [plot for plot in plots], ncols = 2))