Search code examples
pythondata-visualizationinteractivealtair

Apply filter transform to Altair plot while keeping all legend categories


I'm trying to make a stacked bar chart with an interactive legend. I want to be able to select multiple categories from the legend, which will filter the categories shown in the stacked bars.

This is what I have so far:

import altair as alt
import pandas as pd


df = pd.DataFrame(
    {
        "name": ["Sarah", "Sarah", "Sarah", "Sarah", "Nick", "Nick", "Nick"],
        "level": ["A", "B", "B", "C", "A", "B", "C"],
        "id": range(7),
    }
)

selection = alt.selection_multi(fields=['level'], bind='legend')

alt.Chart(df).mark_bar().encode(
    x="count(id)",
    y="name",
    color=alt.Color(
        "level",
    ),
).add_selection(
    selection
).transform_filter(
    selection
)

enter image description here

This generates an interactive plot, but when I click on one category in the legend, the other categories disappear from the legend.

I'd like all categories in the legend to stay visible, so I can select multiple categories (such as "B" and "C") using shift-click, and the stacked bar chart will include all the selected categories. Is there any way I can do this?


Solution

  • One direct way to do this is to specify the domain in the color scale:

        color=alt.Color(
            "level", scale=alt.Scale(domain=['A', 'B', 'C'])
        ),
    

    enter image description here

    Alternatively, if you want to do it in a way that does not require static specification of the domain, one trick you can use is to layer a transparent version of the full chart behind the filtered version. For example:

    base = alt.Chart(df).mark_bar().encode(
        x="count(id)",
        y="name",
        color=alt.Color(
            "level",
        ),
    )
    
    background = base.mark_bar(opacity=0)
    foreground = base.add_selection(
        selection
    ).transform_filter(
        selection
    )
    
    background + foreground
    

    enter image description here