为bokeh中的复选框实现JavaScript回调?

时间:2019-06-10 12:48:12

标签: javascript python pandas callback bokeh

简短版本:带有JS回调的bokeh复选框以绘制数据帧的子集吗?

长版本:答案here为多选提供了很好的解释,但我想对复选框做同样的事情。我所有的数据都在一个pandas数据框中,并且我正在使用复选框选择该数据框的位进行绘图。我相信使用复选框可以与cb_obj.active有关,但是我不确定如何使它起作用。特别是,我的绘图包括彩色矩形和文本,其所有信息(位置,文本,颜色)均取自我的数据框。而且我不知道必须在回调中完成多少绘制,以及在外部进行多少绘制。

据我所知,回调函数会调用函数进行实际绘制。

我知道我应该举一个最小的例子,但是我想不出如何简化我的代码...所以目前,我真正想要的只是一个使用带有JavaScript回调的复选框的示例。绘制数据框的子集。一定有人这样做了,但是我还没有找到一个例子!谢谢。

2 个答案:

答案 0 :(得分:0)

这里是一个例子:

from bokeh.plotting import figure, show, output_notebook
from bokeh.models import Slider, CheckboxGroup, CustomJS, ColumnDataSource, CDSView
from bokeh.models.filters import CustomJSFilter
from bokeh.layouts import row
from bokeh.transform import factor_cmap
from bokeh.palettes import Category10_10
output_notebook()

您可以使用CustomJSFilter计算要显示的行的指数:

from bokeh.sampledata import iris
source = ColumnDataSource(data=iris.flowers)
species = iris.flowers.species.unique().tolist()
checkboxes = CheckboxGroup(labels=species, active=list(range(len(species))))
fig = figure()
filter =  CustomJSFilter(code="""
let selected = checkboxes.active.map(i=>checkboxes.labels[i]);
let indices = [];
let column = source.data.species;
for(let i=0; i<column.length; i++){
    if(selected.includes(column[i])){
        indices.push(i);
    }
}
return indices;
""", args=dict(checkboxes=checkboxes, source=source))

checkboxes.js_on_change("active", CustomJS(code="source.change.emit();", args=dict(source=source)))

fig.scatter("sepal_length", "sepal_width", 
            color=factor_cmap("species", Category10_10, species),
            source=source, view=CDSView(source=source, filters=[filter]))
show(row(checkboxes, fig))

enter image description here

答案 1 :(得分:0)

以下是我针对MultiSelect回答的改编版本:

from bokeh.models import CustomJS, ColumnDataSource, CheckboxGroup, Column

from bokeh.plotting import figure, show
import pandas as pd

data = dict(letter = ['A','A','B','C','B','B','A','C','C','B'], 
x = [1, 2, 1, 2, 3, 2, 2, 3, 2, 3], 
y = ['10','20','10','30','10','40','10','30','10','40'])
data = pd.DataFrame(data)

data_source = ColumnDataSource(data)
source = ColumnDataSource(dict(x = [], y = []))

plot = figure()
plot.circle('x', 'y', line_width = 2, source = source)

callback = CustomJS(args = {'source': source, 'data_source': data_source},
code = """
var data = data_source.data;
var s_data = source.data;
var letter = data['letter'];
var select_vals = cb_obj.active.map(x => cb_obj.labels[x]);
console.log(select_vals);
var x_data = data['x'];
var y_data = data['y'];
var x = s_data['x'];
x.length = 0;
var y = s_data['y'];
y.length = 0;
for (var i = 0; i < x_data.length; i++) {
    if (select_vals.indexOf(letter[i]) >= 0) {
        x.push(x_data[i]);
        y.push(y_data[i]);
        }
}
source.change.emit();
console.log("callback completed");
""")

chkbxgrp = CheckboxGroup(labels = ['A', 'B', 'C'], active=[])
chkbxgrp.js_on_change('active', callback)
layout = Column(chkbxgrp, plot)
show(layout)

备注:

  • 该回调在Internet Explorer中无法使用,因为它使用了IE不支持的箭头功能。如果这是一个问题,则需要 使用箭头功能以外的其他方式进行映射
  • 正如bigreddot用户在您提到的答案中说的那样,这也可以使用CDSView并使用自定义的Filter来完成,因为GroupFilter不支持多个值