Search code examples
pythonaltair

Trying to plot a time series with ordinal data in Altair


I'm slowly understanding a problem that I have with a time series plot involving data over three financial years.

Here's some sample data.

    Financial Year  Month   Category    Value   FY
0   2022-04-01  2023    Apr Total   53,616.40   2022-2023
1   2022-05-01  2023    May Total   56,652.97   2022-2023
2   2022-06-01  2023    Jun Total   41,240.00   2022-2023
3   2022-07-01  2023    Jul Total   37,039.38   2022-2023
4   2022-08-01  2023    Aug Total   46,683.56   2022-2023

I can plot the timeseries like this:

# Create the Altair chart
chart = alt.Chart(marketing_block_melted).mark_bar().encode(
    x=alt.X('Date:T', sort=FY_MONTH_SHORT_ORDER),
    y=alt.Y('Value:Q'),
    color='FY:N',
    tooltip=['Date:T', 'Value:Q', 'Financial Year:N']
).properties(
    width=800,
    height=400,
    title='Year-by-Year Comparison of Financial Data'
)
chart

[![enter image description here][1]][1]

Which is fine. But I want to plot each year's data across months from April to March so I can compare month finances for a month across years. [1]: https://i.sstatic.net/6p7AP7BM.png


Solution

  • Yes, this can be done by setting a time unit on the x encoding and sorting by the fiscal year month. Here's an example with stock data:

    enter image description here

    import altair as alt
    import polars as pl
    from vega_datasets import data
    
    source = data.stocks()
    
    df = pl.DataFrame(source).filter(
        pl.col("symbol") == "AAPL", 
    ).with_columns(
        fy=pl.col("date").dt.year() - pl.when(pl.col("date").dt.month() <= 3).then(1).otherwise(0),
        fy_month=pl.col("date").dt.month() - 3 + pl.when(pl.col("date").dt.month() <= 3).then(12).otherwise(0)
    ).filter(
        pl.col("fy").is_in([2004, 2005, 2006])
    )
    
    alt.Chart(df).mark_bar().encode(
        x=alt.X("date:O", timeUnit="month", sort=alt.EncodingSortField("fy_month")),
        xOffset=alt.XOffset("fy:N"),
        y="mean(price)",
        color=alt.Color("fy:N")
    ).properties(width=400)