Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

213
Views
Bokeh models to plot interactive addition of columns using CustomJS and CheckboxGroup

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!

about 4 years ago · Juan Pablo Isaza
1 answers
Answer question

0

Here is a solution for your problem using lines to plot the data.

The idea in the JS part is, to initialize for every click your values for combined with 0 and then loop over all selected labels given through the values in active and add them to combined.

This is done in two cascaded for loops.

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)

Output

sum

Comment

To add a legend label for each line renderer a legend_label="value" to p.line().

about 4 years ago · Juan Pablo Isaza Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!