Search code examples
python-3.xpandasbokehbokehjs

CheckboxGroup in Bokeh to plot additive plots


I want to use a checkbox group to represent linearly additive sources. The idea is for the user to be able to turn on or turn off multiple sources of different kinds to display a plot with added values.

I implemented a dropdown version where the data had the different combinations as columns of the dataframe so the CustomJS part of the code was as follows:

 callback = CustomJS(args={'source':source},code="""
        // print the selectd value of the select widget - 
        // this is printed in the browser console.
        // cb_obj is the callback object, in this case the select 
        // widget. cb_obj.value is the selected value.
        console.log(' changed selected option', cb_obj.value);

        // create a new variable for the data of the column data source
        // this is linked to the plot
        var data = source.data;

        // allocate the selected column to the field for the y values
        data['A1'] = data[cb_obj.value];

        // register the change - this is required to process the change in 
        // the y values
        source.change.emit();
""")

I don't know Javascript well enough. Is there a way to just give a dataframe as follows:

Wave    A    B   C
340    77   70  15
341    80   73  15
342    83   76  16
343    86   78  17

And have checkbox group buttons where if the user selects 'A' and 'C' the plot changes to a plot of Wave vs. A+C and vice versa for removing the selection.

I found a similar question, but it just shows/hides the plots and doesn't change the data: https://stackoverflow.com/a/38706613/8565759

I have way too many selections in the real data to manually assign combinations.


Solution

  • Yes, you can achieve this with the CheckboxGroup - you can use the active attribute of CheckboxGroup to select the correct columns to add to the combined plot. Here is a complete example with the data you provided:

    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 = ['Wave', '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);
        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;
            }
        }
        data.combined=combined;
        source.change.emit();
    """)
    checkbox_group = CheckboxGroup(labels=names[1:], active=[], callback=callback)
    p = figure(width=400, height=400)
    p.line(x='Wave', y='combined', source=source)
    show(layout([[widgetbox(checkbox_group)], [p]]))