Grey out Heatmap

I am using a heatmap for one of my charts in a dashboard and I am using it along with a dropdown. I want to make the heatmap responsive to the option selected in the dropdown.

Ideally, what I had in mind was to keep the column corresponding to the option selected coloured and grey out everything else (all other columns in the heatmap) to highlight the selected option. However, any other workarounds, like a border being made on that column, work too.

This is something I’m not sure how I can achieve. Any help would be appreciated.

More information is needed. Are you drawing this heatmap using individual rect or quad (or bar) glyphs? Or is this a scalar array that has been colomapped into an image? Generally speaking, the best, most efficient way to leverage the expertise in this forum is to provide actual code, i.e. a Minimal Reproducible Example.

I’m using code that is extremely similar to the Heatmap section in this link Handling categorical data — Bokeh 2.3.3 Documentation

Here’s a modification of that same heatmap that filters based on year, which has the effect of column-highlighting you describe.

import pandas as pd

from bokeh.io import show
from bokeh.layouts import column
from bokeh.models import (BasicTicker, ColorBar, ColumnDataSource,
                          CustomJS, LinearColorMapper, 
                          PrintfTickFormatter, Select)
from bokeh.plotting import figure
from bokeh.sampledata.unemployment1948 import data
from bokeh.transform import transform

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

# 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)

# this is the colormap from the original NYTimes plot
colors = ["#75968f", "#a5bab7", "#c9d9d3", "#e2e2e2", "#dfccce", "#ddb7b1", "#cc7878", "#933b41", "#550b1d"]
mapper = LinearColorMapper(palette=colors, low=df.rate.min(), high=df.rate.max())

p = figure(plot_width=800, plot_height=300, title="US unemployment 1948—2016",
           x_range=list(data.index), y_range=list(reversed(data.columns)),
           toolbar_location=None, tools='', x_axis_location="above")

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

color_bar = ColorBar(color_mapper=mapper,
                     ticker=BasicTicker(desired_num_ticks=len(colors)),
                     formatter=PrintfTickFormatter(format="%d%%"))

p.add_layout(color_bar, 'right')

year_filter = CustomJS(args=dict(source=source), code="""
    var selected_year = cb_obj.value
    
    // clear out previously selected values
    source.selected.indices = []

    // add every row (square) that matches the year from the Select to our source.selected.indices
    source.data.Year.forEach((source_year, i) => {
        console.log(`selected year is ${selected_year} and source year is ${source_year} and index is ${i}`)
        if (source_year == parseInt(selected_year)) {
          console.log("match!")
          source.selected.indices.push(i)
        }
    });

    // update the plot
    source.change.emit()   
""")

p.axis.axis_line_color = None
p.axis.major_tick_line_color = None
p.axis.major_label_text_font_size = "7px"
p.axis.major_label_standoff = 0
p.xaxis.major_label_orientation = 1.0

year_select = Select(options=df.Year.unique().tolist())
year_select.js_on_change("value", year_filter)

col = column(children=[p, year_select])

show(col)

Try it out and let me know if you have any questions about what’s going on in the code. One thing I did not include was a “resetter” option in the drop-down, to go back to a state where no particular column is selected, but that would be a nice add.

1 Like

This is great, thank you! A follow up question here - what I am exactly trying to do is update 2 plots with the value selected in the dropdown. I have another piechart for which I have a Bokeh callback function (NOT CustomJS) because I need to perform some Pandas stuff before I alter the plot. How can I combine the JS callback you have here and my Bokeh callback function so that both charts are updated whenever a value is selected in the dropdown?

Bumping my reply above