Luckasino
Luckasino

Reputation: 424

Second independent axis in Altair

I would like to add a second (independent) x-axis to my figure, demonstrating a month for a given week.

Here is my snippet:

import pandas as pd
import numpy as np
from datetime import datetime, timedelta

weeks = list(range(0, 54))
start_date = datetime(1979, 1, 1)
week_dates = [start_date + timedelta(weeks=w) for w in weeks]

years = list(range(1979, 2024))
data = {
    "week": weeks,
    "date": week_dates,
    "month": [date.strftime("%b") for date in week_dates],
}
# Precipitation
data.update({str(year): np.random.uniform(0, 100, size=len(weeks)) for year in years})
df = pd.DataFrame(data)
df["Mean_Precipitation"] = df[[str(year) for year in years]].mean(axis=1)

rain_calendar = alt.Chart(df, title=f"Weekly precipitation volumes").mark_bar().encode(
    alt.X('week:O', title='Week in calendar year', axis=alt.Axis(labelAlign="right", labelAngle=-45)),
    alt.Y(f'1979:Q', title='Rain'),
    alt.Color(f"1979:Q", scale=alt.Scale(scheme='redyellowgreen')),
    alt.Order(f"1979:Q", sort="ascending")).properties(width=850, height=350)

line = alt.Chart(df).mark_line(interpolate='basis', strokeWidth=2, color='#FB0000').encode(
    alt.X('week:O'),
    alt.Y('Mean_Precipitation:Q'))

month_labels = alt.Chart(df).mark_text(
    align='center', baseline='top', dy=30
).encode(
    alt.X('week:O', title=None), text='month:N')

combined_chart = alt.layer(rain_calendar, line, month_labels).resolve_scale(x='shared').configure_axisX(
    labelAlign="right", labelAngle=-45, titlePadding=10, titleFontSize=14)\
    .configure_axisY(titlePadding=10, titleFontSize=14).configure_bar(binSpacing=0)

So far, this is the best outcome that I've got:

enter image description here

It is a topic related to discussed one there: Second x axis or x labels

Upvotes: 1

Views: 45

Answers (2)

kgoodrick
kgoodrick

Reputation: 868

This can be done by using the timeUnit parameter of the x encoding along with the axis format and, optionally, labelExpr parameters.

Also, Altair works best with long form data and can easily handle the aggregation for you, so I have updated the data generation with this in mind. If your real data is already in short form, you can use pandas melt function. More details are in the documentation

Chart with week number and month

import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import altair as alt

start_date = datetime(1979, 1, 1)
end_date = datetime(2024, 12, 31)
df = pd.DataFrame(pd.date_range(start_date, end=end_date, freq="W"), columns=['date'])
df['Precipitation'] = np.random.uniform(0, 100, size=len(df))

base = alt.Chart(df, title="Weekly precipitation volumes").encode(
    x=alt.X(
        "date:O",
        timeUnit="week",
        title="Week in calendar year",
        axis=alt.Axis(
            format='W %-W/%b', 
            labelExpr="split(datum.label, '/')"
        ),
    ),
)

rain_calendar = (
    base.mark_bar()
    .encode(
        y=alt.Y("Precipitation:Q", title="Rain"),
        color=alt.Color("Precipitation:Q", scale=alt.Scale(scheme="redyellowgreen"), title="1979"),
    )
    .transform_filter(alt.expr.year(alt.datum['date']) == 1979)
    .properties(width=850, height=350)
)

line = base.mark_line(interpolate="basis", strokeWidth=2, color="#FB0000").encode(
    y="mean(Precipitation):Q"
)

combined_chart = (
    alt.layer(rain_calendar, line)
    .configure_axisX(
        titlePadding=10, titleFontSize=14
    )
    .configure_axisY(titlePadding=10, titleFontSize=14)
    .configure_bar(binSpacing=0)
)

combined_chart

Upvotes: 1

tetris programming
tetris programming

Reputation: 1496

To get the months of the dates on your x-axis it would probably the easiest to use the dates data but only show the months.

the code for this could look like this:

month_labels = alt.Chart(df).mark_text(
    align='center', baseline='top', dy=30).encode(
    alt.X('month(date):O', title=None, ),
    text='month(date):O', )

Upvotes: 0

Related Questions