Mapper doesn't update via js callback dynamically

The color_bar doesn’t update for both the radio and multichoice groups. The change doesn’t get transmitted. Everything else (all the other logic) works fine and console.log(mapper) shows the updated min and max values.


import numpy as np
import pandas as pd
from bokeh.plotting import figure, show
from bokeh.models import ColumnDataSource, ColorBar, HoverTool, CustomJS, Div
from bokeh.models.widgets import RadioGroup, MultiChoice
from bokeh.layouts import row
from bokeh.transform import linear_cmap
from bokeh.palettes import Cividis10


def main():
    our_list = ["SAMD11", "HES4"]
    df1 = pd.DataFrame({'SAMD11': [1, 2, 3], 'HES4': [4, 5, 6], 'BIS': [20, 21, 22], 'umap1': [10, 11, 12], 'umap2': [13, 14, 15], 'index': ['A', 'B', 'C']})
    df1["score"] = np.sum(df1[our_list].values, axis=1)       # Initially
    OPTIONS = sorted(df1.columns.tolist()[:3])
    source = ColumnDataSource(df1[our_list + ["score", "umap1", "umap2", "index"]])
    df2 = pd.DataFrame({'SAMD11': [-1, -2, -3], 'HES4': [-4, -5, -6], 'BIS': [-20, -21, -22], 'umap1': [-10, -11, -12], 'umap2': [-13, -14, -15], 'index': ['A', 'B', 'C']})
    df2['score'] = None
    mapper = linear_cmap(field_name="score",
                               palette=Cividis10,
                               low=min(source.data["score"]),
                               high=max(source.data["score"]))
    p = figure(
        title="UMAP Visualization",
        x_axis_label="umap1",
        y_axis_label="umap2",
        height=800,
        width=800,
        toolbar_location="above",
        tools=[HoverTool(tooltips=[("Cell Name", "@index")], mode="mouse")])
    p.scatter("umap1",
              "umap2",
              source=source,
              color=mapper,
              line_color=None)
    color_bar = ColorBar(color_mapper=mapper["transform"], width=8)
    p.add_layout(color_bar, "right")

    radio_group = RadioGroup(labels=["File 1", "File 2"], active=0)
    multichoice_group = MultiChoice(value=our_list, options=OPTIONS, width=200, search_option_limit=10)

    radio_callback = CustomJS(
        args=dict(source=source, source1=df1.to_dict(orient='list'), source2=df2.to_dict(orient='list'), multichoice_group=multichoice_group, mapper=mapper),
        code="""
        var our_list = multichoice_group.value;
        var sourceData = this.active === 0 ? source1 : source2;
        var cols = our_list.concat(["score", "umap1", "umap2", "index"]);
        for (var i = 0; i < cols.length; i++) {
            var col = cols[i];
            source.data[col] = sourceData[col];}

        var sumArray = [];
        for (var i = 0; i < source.data['index'].length; i++) {
            var sum = 0;
            for (var j = 0; j < our_list.length; j++) {
                sum += source.data[our_list[j]][i];}
            sumArray.push(sum);
            }    
      
        var updates = { score: [] };
        for (var i = 0; i < sumArray.length; i++) {
            updates.score.push([i, sumArray[i]]);}
        source.patch(updates);
        console.log(source.data);

        mapper.low = parseFloat(Math.min.apply(null, sumArray));
        mapper.high = parseFloat(Math.max.apply(null, sumArray));
        console.log(mapper);
        """)
    multichoice_callback = CustomJS(args=dict(source=source, source1=df1.to_dict(orient='list'), source2=df2.to_dict(orient='list'), radio_group=radio_group, mapper=mapper),
        code="""
        var our_list = this.value;
        var sourceData = radio_group.active === 0 ? source1 : source2;
        var cols = our_list.concat(["score", "umap1", "umap2", "index"]);

        for (var i = 0; i < cols.length; i++) {
            var col = cols[i];
            if (!(col in source.data)) {
                source.data[col] = sourceData[col];}
            }
        for (var col in source.data) {
            if (col != 'level_0' && !cols.includes(col)) {
                delete source.data[col];}
            }  

        var sumArray = [];
        for (var i = 0; i < source.data['index'].length; i++) {
            var sum = 0;
            for (var j = 0; j < our_list.length; j++) {
                sum += source.data[our_list[j]][i];}
            sumArray.push(sum);
            }         

        var updates = { score: [] };
        for (var i = 0; i < sumArray.length; i++) {
            updates.score.push([i, sumArray[i]]);}
        source.patch(updates);
        console.log(source.data);       

        mapper.low = parseFloat(Math.min.apply(null, sumArray));
        mapper.high = parseFloat(Math.max.apply(null, sumArray));
        console.log(mapper);
        """)

    radio_group.js_on_change("active", radio_callback)
    multichoice_group.js_on_change("value", multichoice_callback)
    layout = row([multichoice_group, radio_group, p])
    show(layout)

if __name__ == "__main__":
    main()

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