【问题标题】:Implementing JavaScript callback for checkboxes in bokeh?为散景中的复选框实现 JavaScript 回调?
【发布时间】:2019-06-10 12:48:12
【问题描述】:

短版: 带有 JS 回调的散景复选框来绘制数据框的子集?

加长版:答案here 对多选给出了很好的解释,但我想对复选框做同样的事情。我所有的数据都在一个 pandas 数据框中,我使用复选框来选择该数据框的位进行绘图。我相信复选框与cb_obj.active 有关,但我非常不确定如何让它工作。特别是我的情节包括彩色矩形和文本,所有信息:位置,文本,颜色,来自我的数据框。而且我不知道在回调中必须进行多少绘图,以及在外部进行多少。

据我所知,回调调用了一个函数来进行实际的绘图。

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

【问题讨论】:

    标签: javascript python pandas callback bokeh


    【解决方案1】:

    这是一个例子:

    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))
    

    【讨论】:

    • 这正是我正在寻找的解决方案! CustomJSFillter 和 CDSView 的组合(我以前都没有遇到过)看起来就像我想要的那样,而且非常整洁和简单。真的非常感谢。
    • 还有一个问题:用这种方法添加“全选”和“全部清除”回调的最简单方法是什么?我是否需要为它们编写单独的 JS 函数,或者我可以简单地以某种方式引用初始设置?
    【解决方案2】:

    这是您提到的我对 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 使用自定义过滤器来完成,因为 GroupFilter 不支持多个值

    【讨论】:

    • 非常感谢 - 事实上 HYRY 在下面的回答就是这样做的,它似乎非常适合我的目的。散景很棒,但是(至少对我来说,作为一个初学者)很难找到合适的元素组合来获得我想要的效果。
    猜你喜欢
    • 2017-01-26
    • 2014-03-15
    • 2018-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多