Search code examples
pythonaltairvega-lite

Is it possible to use altair transform filters with the temporal encoding type?


Altair transform filters does not seem to work for me with the temporal encoding.

I am trying to build a single timeseries line chart where on hover, a date is selected and a breakdown by category is displayed in another bar chart next to it, however, the filtering does not seem to work.

My selector is

nearest = alt.selection(type='single', nearest=True, on='mouseover',
                        fields=['date'], empty='none')

and my filter is

transform_filter(alt.datum.date == nearest.date)

is anything obviously wrong above?

Here is also a self-contained example illustrating the issue on dummy data:

import altair as alt
import pandas as pd
import numpy as np

dates = pd.date_range('2020-01-01', '2020-06-01', freq='bm')

df1 = pd.Series(np.random.rand(len(dates)), index=dates).reset_index()
df1.columns = ['date', 'value']

df2 = pd.DataFrame({'Cat A': [1,2,3,4,5], 'Cat B': [4,5,6,7,8]}, index=dates).stack().reset_index()
df2.columns = ['date', 'category', 'category_value']

nearest = alt.selection(type='single', nearest=True, on='mouseover',
                        fields=['date'], empty='none')

c1 = alt.Chart(df1).mark_line().encode(
  x='date:T',
  y='value:Q',
)

selectors = alt.Chart(df1).mark_point().encode(
    x='date:T',
    y='value:Q',
    opacity=alt.condition(nearest, alt.value(1), alt.value(0))
).add_selection(
    nearest
)


c2 = alt.Chart(df2).mark_bar().encode(
x='category',
y='category_value').transform_filter(alt.datum.date == nearest.date)

(c1 + selectors) | c2 

Solution

  • There are two issues here:

    • you are comparing a raw date string to parsed timestamp. You'll need to call toDate() on the raw data to rectify this.
    • The python == translates to javascript === within expressions; becuase nearest.date returns a list, we need to use the less restrictive == operator. This is not available via alt.expr, so we can pass a vega expression string directly instead.

    If you use this filter, it should take care of those issues:

    .transform_filter(
        f"toDate(datum.date) == {nearest.date}"
    )