Search code examples
pythonvega-litealtair

Filter altair heatmap with heat shading and text value


I'm trying to create a heatmap using the Altair lib, but I'm looking to filter my data with a slider for different views. The slider works fine with the standard color only heatmap, but when I try to add text to the boxes to describe the values in each cell I get the javascript error below. (Adding the text to the heatmap works fine without any filter slider.)

import altair as alt
import pandas as pd
source = pd.DataFrame({'year':[2017, 2017, 2018, 2018],
                       'age':[1, 2, 1, 2],
                       'y':['a', 'a', 'a', 'a'],
                       'n':[1, 2, 3, 4]})

slider = alt.binding_range(min=2017, max=2018, step=1)
select_year = alt.selection_single(name="my_year_slider", fields=['year'], bind=slider)
base = alt.Chart(source).add_selection(select_year).transform_filter(select_year)

heatmap = base.mark_rect().encode(
    x='age:O',
    y='y:O',
    color='n:Q')

text = base.mark_text(baseline='middle').encode(
    x='age:O',
    y='y:O',
    text='n:Q')

heatmap + text

This returns Javascript Error: Duplicate signal name: "my_year_slider_tuple"


Solution

  • Because you added the selection to the base chart and then layered two copies of it, the selection is defined twice. The solution is to only define the selection once; something like this:

    import altair as alt
    import pandas as pd
    source = pd.DataFrame({'year':[2017, 2017, 2018, 2018],
                           'age':[1, 2, 1, 2],
                           'y':['a', 'a', 'a', 'a'],
                           'n':[1, 2, 3, 4]})
    
    slider = alt.binding_range(min=2017, max=2018, step=1)
    select_year = alt.selection_single(name="my_year_slider", fields=['year'], bind=slider)
    
    base = alt.Chart(source).encode(
        x='age:O',
        y='y:O',
    ).transform_filter(select_year)
    
    heatmap = base.mark_rect().encode(color='n:Q').add_selection(select_year)
    
    text = base.mark_text(baseline='middle').encode(text='n:Q')
    
    heatmap + text