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:
It is a topic related to discussed one there: Second x axis or x labels
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
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