Search code examples
pythonaltairvega-lite

How to use conditional selection for properties in Altair?


I'm trying to have a line plot respond to selections on the legend and update some parts of itself accordingly.

Below I have a few failed attempts : (

What I want to achieve is the following: the legend should be split into the available options for 'Country' and 'Mode', as in chart0. However, I would like the chart to only plot the data relative to the selected mode(s). I would also like to change Axis labels, scale and other properties when certain options are selected. I think I'm not using alt.condition() correctly.

import pandas as pd
import altair as alt

df1 = pd.DataFrame({'foo':[2,4,3],'bar':[1,2,1]})
df2 = pd.DataFrame({'foo':[20,30,45],'bar':[50,10,40]})
df = pd.concat([df1, df2], axis=1)
col = pd.MultiIndex.from_arrays([['A', 'A', 'B', 'B'], ['foo','bar','foo','bar']])
df.columns = col
df = df.reset_index().melt('index', var_name=['Mode','Country'], value_name='myvalue').dropna()

country_selection = alt.selection_multi(fields=['Country'], bind='legend')
mode_selection = alt.selection_multi(fields=['Mode'], bind='legend')


chart0 = alt.Chart(df).mark_line().encode(
    x='index:Q',
    y = alt.Y('myvalue:Q', title='A'),
    color=alt.condition(country_selection, alt.Color('Country:N'), alt.value('lightgray')),
    opacity='Mode:N'
).add_selection(country_selection).add_selection(mode_selection).properties(width=300, height=300)


chart1 = alt.Chart(df).mark_line().encode(
    x='index:Q',
    y = alt.Y('myvalue:Q', title='A'),
    color=alt.condition(country_selection, alt.Color('Country:N'), alt.value('lightgray')),
).add_selection(country_selection).add_selection(mode_selection).transform_filter(mode_selection).properties(width=300, height=300)



chart2 = alt.Chart(df).mark_line().encode(
    x='index:Q',
    y = alt.Y('myvalue:Q', title=alt.condition(mode_selection, 'A', 'B')),
    color=alt.condition(country_selection, alt.Color('Country:N'), alt.value('lightgray')),
    opacity='Mode:N'
).add_selection(country_selection).add_selection(mode_selection).transform_filter(mode_selection).properties(width=300, height=300)
    

Solution

  • If you want only the selected data to be plotted (with the axis adjusting based on the content of the data), you can use a transform_filter to limit the chart to showing the selected data.

    In order to have the unselected data appear on the legend, it is important to explicitly set the legend's scale domain:

    country_selection = alt.selection_multi(fields=['Country'], bind='legend')
    mode_selection = alt.selection_multi(fields=['Mode'], bind='legend')
    
    alt.Chart(df).mark_line().encode(
        x='index:Q',
        y = alt.Y('myvalue:Q', title='A'),
        color=alt.Color('Country:N', scale=alt.Scale(domain=list(df.Country.unique()))),
        opacity=alt.Opacity('Mode:N', scale=alt.Scale(domain=list(df.Mode.unique())))
    ).add_selection(
        country_selection,
        mode_selection
    ).transform_filter(
        country_selection
    ).transform_filter(
        mode_selection
    )
    

    enter image description here