Apply filter transform to Altair plot while keeping all legend categories

Viewed 501

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?

1 Answers

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

Related