CustomJSFilter for filtering CheckboxGroup-selection

I am creating a dashboard where I want to display different data based on selections the user had made in a RadioButtonGroup (for the ‘Cells’) and a CheckboxGroup (for the ‘Temperatures’). The CustomJS-callback I have written for the RadioButtonGroup works as intended on its own, but when I try to also imlement the CustomJSFilter for the temperatures no data appears in the figure.

Because of how I am planning to expand the dashboard I have created two classes in order to have fewer global variables. I have tried letting source be a global variable (outside of the class), but this did not fix the issue.

I guess the error comes from the way I filter the data in the CustomJSFilter, but have thus far been unable to fix the problem. Any help would be much appreciated!

from bokeh.io import show, curdoc
from bokeh.models import CustomJS, CheckboxGroup, ColumnDataSource, CDSView, GroupFilter, CustomJSFilter, Div, RadioButtonGroup
from bokeh.plotting import figure
from bokeh.layouts import column, row, layout
from bokeh.palettes import Plasma256 
import pandas as pd        


#### Classes ####
class CurrentData:
    # def __init__(self, all_data_files, all_data, inputs_selected, sources, dicts):
    def __init__(self, source, df, CELL_SELECTOR_LABEL, TEMPERATURE_SELECTOR_LABEL):
        self.source = source
        self.df = df
        self.CELL_SELECTOR_LABEL = CELL_SELECTOR_LABEL
        self.TEMPERATURE_SELECTOR_LABEL = TEMPERATURE_SELECTOR_LABEL 


class CurrentPlot:
    def __init__(self, cell_plots, cell_view, cell_selector, temperature_selector):
        self.cell_plots = cell_plots
        self.cell_view = cell_view
        self.cell_selector = cell_selector
        self.temperature_selector = temperature_selector
    
    def instert_glyphs(self, data_class, p_nyquist, filt):
        for cell_color_counter, cell_name in enumerate(data_class.CELL_SELECTOR_LABEL):
            cell_color = Plasma256[1 + cell_color_counter*int(256/len(data_class.CELL_SELECTOR_LABEL))]
            view = CDSView(filter=GroupFilter(column_name="cell_name", group=cell_name) & filt)

            self.cell_plots[cell_name] = [
                p_nyquist.circle(x='realvalues', y='imaginaryvalues', source=data_class.source, view=view, size=7, color=cell_color, legend_label=cell_name)       
            ]

#### Initializing the classes ####
currentdata = CurrentData(None, None, None, None)
currentplot = CurrentPlot({}, {}, None, None)

data = [
    [ 0.14, 35.4, 1.72, 34.6, 2.8, 'Cell 2', '30°C'],
    [ 0.16, 32.4, 1.9, 31.6, 4.8, 'Cell 3', '50°C']
]
currentdata.df = pd.DataFrame(data = data, columns=['frequencies', 'realvalues', 'imaginaryvalues', 'magnitude', 'phase_angle', 'cell_name', 'temp'])

# Creating the source
currentdata.source = ColumnDataSource(data = {})
currentdata.source.data = {col_name : currentdata.df[col_name].tolist() for col_name in currentdata.df.columns}

# Lists for the labels
currentdata.CELL_SELECTOR_LABEL = currentdata.df['cell_name'].unique().tolist()
currentdata.TEMPERATURE_SELECTOR_LABEL = currentdata.df['temp'].unique().tolist()

#### Widget setup ####
currentplot.cell_selector = RadioButtonGroup(labels=['All'] + currentdata.CELL_SELECTOR_LABEL, active=0)
temperature_selector_label = Div(text="""Temperature:""", styles={'font-size': '16pt'}, width=300, height=30)
currentplot.temperature_selector = CheckboxGroup(labels=currentdata.TEMPERATURE_SELECTOR_LABEL, active=list(range(len(currentdata.df['temp'].unique()))))

#### Plots ####
p_nyquist = figure(height = 800, width = 1200, x_axis_label="Re(Z / \u03A9 cm\u00b2)",
            y_axis_label="-Im(Z / \u03A9 cm\u00b2)", match_aspect = True, aspect_scale = 1, outline_line_width = 1, outline_line_color = '#000000')

#### Filter ####
filt = CustomJSFilter(args=dict(
    temperature_selector = currentplot.temperature_selector,
    temperature_selector_dict = {i:x for i,x in enumerate(currentdata.df['temp'].unique())}
    ), 
    code = '''    
    const temperature_selector_active = temperature_selector.active.map(x=>temperature_selector_dict[x])
    const sel_indices = [];
    for (let i = 0; i < source.get_length(); i++){
        if (temperature_selector_active.includes(source.data['temp'][i])) {
            sel_indices.push(true);
        } else {
            sel_indices.push(false);
        }
    }
    return sel_indices;

    '''                  
)
        
# Adding glyphs to plot
currentplot.instert_glyphs(currentdata, p_nyquist, filt)

# Callback for cell selection
currentplot.cell_selector_callback = CustomJS(args=dict(cell_selector=currentplot.cell_selector, CELL_SELECTOR_LABEL=currentdata.CELL_SELECTOR_LABEL, cell_plots=currentplot.cell_plots), code="""
    var selectedIndices = cell_selector.active;
    if (selectedIndices === 0) {
        for (var i = 0; i < CELL_SELECTOR_LABEL.length; i++) {
            var cell_name = CELL_SELECTOR_LABEL[i];
            cell_plots[cell_name][0].visible = true;
        }
    } else {
        for (var i = 0; i < CELL_SELECTOR_LABEL.length; i++) {
            var cell_name = CELL_SELECTOR_LABEL[i];
            cell_plots[cell_name][0].visible = (i + 1 === selectedIndices);
        }
    }
"""
)
currentplot.cell_selector.js_on_change('active', currentplot.cell_selector_callback) 
currentplot.temperature_selector.js_on_change('active' ,CustomJS(args=dict(src=currentdata.source),code='''src.change.emit()'''))


#### Layout ####
lay_out = layout(
    column(
        currentplot.cell_selector,
        row(
            p_nyquist,
            column(
                temperature_selector_label,
                currentplot.temperature_selector,
                )
        )
    )
)

curdoc().add_root(lay_out)
curdoc().title = "Test"

HI @elling2000 I have to be honest and say that the structure of your code seems a bit overly complicated. I am willing to try to help but rather than approaching it from the direction of debugging 150 lines of code with an unknown number of issues, I’d ask to turn things around. Start with something simpler, much simpler, that works, e.g a single plot with all the data, and work incrementally to add small features one at a time.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.