Search code examples
altair

How to format the last line segment based on a datetime column?


I'm trying to make the final segment of a line plot dashed to indicate incomplete data. From what I can tell I should be able to do this using a condition on strokeDash. However I can't figure out how to get the condition predicate to work using a datetime field.

alt.Chart(rates)
    .mark_line(point=True)
    .encode(
        x=alt.X("start_date:T", scale=alt.Scale(nice="week")),
        y="install_rate",
        strokeDash=alt.condition(
            f"datum.start_date > toDate({start_dates[-2].isoformat()})",
            alt.value([5, 5]),  # dashed line: 5 pixels  dash + 5 pixels space
            alt.value([0]),  # solid line
            )
        )

This gives me an error:

Error: Illegal callee type: MemberExpression

Solution

  • You can fix the error you are encountering by making sure that pandas reads in the dates as a temporal data type:

    import pandas as pd
    import altair as alt
    
    
    rates = pd.DataFrame({
        'start_date': pd.to_datetime(['2022-05-06', '2022-05-13', '2022-05-19', '2022-05-25']),
        'install_rate': [0.05, 0.06, 0.08, 0.09],
    })
    
    alt.Chart(rates).mark_line(point=True).encode(
        x=alt.X("start_date:T"),
        y="install_rate",
        color=alt.condition(
            f"datum.start_date > toDate('2022-05-19')",
            alt.value('blue'),
            alt.value('red')
        )
    )
    

    enter image description here

    However, as you can see the lines is not amenable to modifications via a condition. I think this is because it is considered a single continuous mark whereas the points are split up and can be changed individually.

    You could group the line by creating a new separate field and grouping by it, which creates two separate lines.

    rates['above_threshold'] = rates['start_date'] > '2022-05-13'
    alt.Chart(rates).mark_line(point=True).encode(
        x=alt.X("start_date:T"),
        y="install_rate",
        color='above_threshold')
    

    enter image description here

    However, that causes issues with the gap as you can see above. I think for your case the easiest might be to layer two charts with filter transforms:

    base = alt.Chart(rates).encode(
        x=alt.X("start_date:T"),
        y="install_rate",
    )
    
    base.mark_line(strokeDash=[5, 5]).transform_filter(
        f"datum.start_date > toDate('2022-05-19')"
    ) + base.mark_line().transform_filter(
        f"datum.start_date < toDate('2022-05-20')"
    )
    

    enter image description here