☰Read-only archive⌕
Looking for help with Bokeh?

This is a read-only archive. Ask questions and join the conversation on GitHub.

Visit Community Support on GitHub →
Archived discussions · Captured October 28, 2026

Heatmap dynamic change on callback

Community Supportcustomjs
Zana
Zana

Hi,

I am making a heatmap and the user selects what data to look at via a multiselect tool. They can also change the heatmap start and end. But when the user changes the heatmap high and end, the labels disappear. What am I doing wrong?

from math import pi

import pandas as pd

from bokeh.io import show
from bokeh.models import BasicTicker, ColorBar, LinearColorMapper, PrintfTickFormatter, BooleanFilter, MultiSelect, CDSView, TextInput, ColumnDataSource, CustomJS
from bokeh.plotting import figure
from bokeh.layouts import row, column
from bokeh.sampledata.unemployment1948 import data

data['Year'] = data['Year'].astype(str)
data = data.set_index('Year')
#data.drop('Annual', axis=1, inplace=True)
data.columns.name = 'Month'

years = list(data.index)
months = list(data.columns)

# reshape to 1D array or rates with a month and year for each row.
df = pd.DataFrame(data.stack(), columns=['rate']).reset_index()
source = ColumnDataSource(df)

year_filter= BooleanFilter([True if x == '2000' else False for x in source.data['Year']])
hmLow = TextInput(title = 'Heatmap low:', value = str(df.rate.min()))
hmMax = TextInput(title = 'Heatmap high:', value = str(df.rate.max()))

view = CDSView(source=source, filters=[year_filter])

year_select = MultiSelect(title= 'Year: ', value =['2000'],  width = 200, height = 200,
                           options=years)

colors = ["#75968f", "#a5bab7", "#c9d9d3", "#e2e2e2", "#dfccce", "#ddb7b1", "#cc7878", "#933b41", "#550b1d"]
mapper = LinearColorMapper(palette=colors, low=df.rate.min(), high=df.rate.max())

TOOLS = "hover,save,pan,box_zoom,reset,wheel_zoom"

p = figure(title="US Unemployment ({0} - {1})".format(years[0], years[-1]),
           x_range=years, y_range=list(reversed(months)),
           x_axis_location="above", plot_width=900, plot_height=400,
           tools=TOOLS, toolbar_location='below',
           tooltips=[('date', '@Month @Year'), ('rate', '@rate%')])

p.grid.grid_line_color = None
p.axis.axis_line_color = None
p.axis.major_tick_line_color = None
p.axis.major_label_text_font_size = "14px"
p.axis.major_label_standoff = 0
p.xaxis.major_label_orientation = pi / 3

p.rect(x="Year", y="Month", width=1, height=1,
       source=source,
       view = view,
       fill_color={'field': 'rate', 'transform': mapper},
       line_color=None)

color_bar = ColorBar(color_mapper=mapper, major_label_text_font_size="7px",
                     ticker=BasicTicker(desired_num_ticks=len(colors)),
                     formatter=PrintfTickFormatter(format="%d%%"),
                     label_standoff=6, border_line_color=None, location=(0, 0))

p.add_layout(color_bar, 'right')

year_select.js_on_change('value', CustomJS(args = dict(f = year_filter, 
                                                       source = source),
                                          code="""\
                                          const val = cb_obj.value;
                                          f.booleans = Array.from(source.data['Year']).map(d =>  val.includes(d != null && d.toString())); 
                                          //source.selected.indices = [...new Set(source.data.index.filter((_, idx) => f.booleans[idx]))];
                                          
                                          source.change.emit();                                      
                                          """))
heatmapchange = CustomJS(args = dict(mapper = mapper, 
                                     hmLow = hmLow,
                                     hmMax = hmMax),
                                          code="""\

                                          console.log(mapper)
                                          mapper.attributes.low = hmLow.value
                                          mapper.attributes.high = hmMax.value
                                          console.log(mapper)                                          
                                          mapper.change.emit()
                                          """)

hmLow.js_on_change('value', heatmapchange)
hmMax.js_on_change('value', heatmapchange)

show(row(p, year_select, column(hmLow, hmMax)))
⌁   Link to post
p-himik
p-himik

Replace the body of the callback with just

mapper.low = parseFloat(hmLow.value);
mapper.high = parseFloat(hmMax.value);

Don’t use attributes. And no need for mapper.change.emit() because you assign the new values to the attributes as opposed to mutating the values inside containers.

⌁   Link to post

End of archived discussion · Replies are disabled