0

I want a CheckboxGroup to represent different columns of a dataframe. The idea is for the user to be able to add multiple column values if they select multiple columns and interactively display the sum as a plot.

I have the following plot from a previous help I got:

from bokeh.io import output_file, show
from bokeh.plotting import figure
from bokeh.layouts import layout, widgetbox
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import CheckboxGroup
import pandas as pd

output_file('additive_checkbox.html')
names = ['Number', 'A', 'B', 'C']
rows = [(340, 77, 70, 15),
        (341, 80, 73, 15),
        (342, 83, 76, 16),
        (343, 86, 78, 17)]
data = pd.DataFrame(rows, columns=names)
data['combined'] = None
source = ColumnDataSource(data)
callback = CustomJS(args=dict(source=source), code="""
    const labels = cb_obj.labels;
    const active = cb_obj.active;
    const data = source.data;
    const sourceLen = data.combined.length;
    const combined=Array(sourceLen).fill(undefined);
    var combined_label = ''

if (active.length > 0) {
    const selectedColumns = labels.filter((val, ind) => active.includes(ind));
    for(let i = 0; i < sourceLen; i++) {
        let sum = 0;
        for(col of selectedColumns){
            sum += data[col][i];
        }
        combined[i] = sum;
    }
// get index positions of active buttons; use that to retrieve labels to build "combined" label string
    for (let j=0; j < active.length; j++) {
        combined_label += labels[active[j]]+'+';
    } 
    combined_label = '('+combined_label.substring(0, combined_label.length - 1)+')';
}
else {  // if there are no active buttons, label as 'none'
  combined_label = '(none)';
}

legend_item.label.value = 'combined '+combined_label;
data.combined=combined;
source.change.emit();
""")
checkbox_group = CheckboxButtonGroup(labels=names[1:], active=[], width=400)
checkbox_group.js_on_change("active", callback)

final_col = column(p, checkbox_group)
show(final_col)

And it is giving me the following error: ValueError: failed to validate CheckboxButtonGroup(id='1223', ...).js_event_callbacks: expected an element of Dict(String, List(Instance(CustomJS))), got CustomJS(id='1222', ...)

I am not good with JS and have no idea what it means!

Update: Now, after frustrating syntax changes that I am not sure if they are accurate, the above error went away but it doesn't plot combined or any columns from the source!

4

1 回答 1

0

这是使用线条绘制数据的问题的解决方案。

JS 部分的想法是,为每次点击初始化您的值,combined使用 0,然后遍历所有labels通过值给定的选定值active并将它们添加到combined.

这是在两个级联for循环中完成的。

import pandas as pd
from bokeh.plotting import figure, output_notebook, show
from bokeh.layouts import column
from bokeh.models import ColumnDataSource, CustomJS
from bokeh.models.widgets import CheckboxButtonGroup
output_notebook()

# data
names = ['Number', 'A', 'B', 'C']
rows = [(340, 77, 70, 15),
        (341, 80, 73, 15),
        (342, 83, 76, 16),
        (343, 86, 78, 17)]
data = pd.DataFrame(rows, columns=names)
data['combined'] = 0
source = ColumnDataSource(data)

# figure
p = figure(width=300, height=300, y_range=(-10, 200))
p.line(x='Number', y='A', color='red', source=source)
p.line(x='Number', y='B', color='green', source=source)
p.line(x='Number', y='C', color='blue', source=source)
p.line(x='Number', y='combined', color='black', line_width=2, source=source)

# JS
callback = CustomJS(args=dict(source=source), code="""
    const labels = cb_obj.labels;
    const active = cb_obj.active;
    const data = source.data;

    // initialize with 0
    for(let i=0; i<data['Number'].length; i++)
        data['combined'][i] = 0
    // add new value for every selected label
    for(let a of active){
        let active_label = labels[a]
        for(let i=0; i<data['Number'].length; i++)
            data['combined'][i] += data[active_label][i]
    }
        
    source.change.emit();
""")
checkbox_group = CheckboxButtonGroup(labels=names[1:], active=[], width=400)
checkbox_group.js_on_change("active", callback)

# output
final_col = column(p, checkbox_group)
show(final_col)

输出

和

评论

为每个line渲染器添加一个图例标签 alegend_label="value"p.line().

于 2022-02-23T18:12:18.363 回答