I am trying to map two lines on a single chart in Altair that change color midway through the plot.
Here is what the plot would look like ideally:
So far I have successfully plot two lines on a single graph. Each line takes a custom color, but the colors do not change at the midway point.
Here's the code: (1) dummy version of my data:
import pandas as pd
from datetime import datetime
import numpy as np
data_list = [(['1', '2']*20)*2,
[0]*20+[1]*20,
np.repeat(pd.date_range(datetime.today(), periods=20).tolist(), 2),
np.random.normal(size=40)
]
data_df = pd.DataFrame(data_list).transpose()
data_df.columns = ['group', 'post', 'datetime', 'value']
(2) Altair code
PROPERTIES = {"height": 200, "width": 800}
line_chart_1 = (
alt.Chart(data_df)
.transform_calculate(group="datum.group == '1' ? 'Group A:1' : 'Group B:1' ")
.transform_filter("datum.post == 0")
.mark_line(opacity=0.8, strokeWidth=3, strokeCap='round')
.encode(
alt.X("datetime:T", title=None),
alt.Y("value:Q", title=None),
alt.Color(
"group:N",
scale=alt.Scale(
domain=("Group A:1", "Group B:1"), range=("#F44E4E", "#0442BF")
),
title=None,
legend=alt.Legend(orient="bottom-left"),
),
)
)
line_chart_2 = (
alt.Chart(data_df)
.transform_calculate(group="datum.group == '1' ? 'Group A:2' : 'Group B:2' ")
.transform_filter("datum.post == 1")
.mark_line(opacity=0.8, strokeWidth=3, strokeCap='round')
.encode(
alt.X("datetime:T", title=None),
alt.Y("value:Q", title=None),
alt.Color(
"group:N",
scale=alt.Scale(
domain=("Group A:2", "Group B:2"), range=("#F20505", "#63CAF2")
),
title=None,
legend=alt.Legend(orient="bottom-left"),
),
)
)
area_chart = (
alt.Chart(data_df)
.mark_rect(opacity=0.5)
.encode(
alt.X("start:T"),
alt.X2("stop:T"),
y=alt.value(0),
y2=alt.value(PROPERTIES["height"]),
fill=alt.Color(
"post:N",
scale=alt.Scale(domain=(0, 1), range=("lightgray", "lightgray")),
legend=None,
),
)
)
alt.layer(area_chart, line_chart_2, line_chart_1).properties(**PROPERTIES)
I also saw this post and tried the following:
line_chart = alt.layer(
alt.Chart().mark_line(),
alt.Chart().mark_line().encode(color='post:N'),
data=data_df
).transform_filter(
'datum.group==="1"',
).encode(
x='datetime:T',
y=alt.Y('value:Q', impute={'value': None}),
)
line_chart_2 = alt.layer(
alt.Chart().mark_line(),
alt.Chart().mark_line().encode(color='post:N'),
data=data_df
).transform_filter(
'datum.group==="2"',
).encode(
x='datetime:T',
y=alt.Y('value:Q', impute={'value': None}),
)
alt.layer(line_chart, line_chart_2)
Which gets me really close, but I cannot figure out how to change the colors of the line segments or add a legend.
Anyone know how to get one of these plots over the finish line? Thanks in advance for your help!
You need to explicitly state that you want independent legends using the resolve_scale
function. I tried running your code but the x
values of your mark_rect
are missing. Using only the line charts, it will go something like this
alt.layer(line_chart_1, line_chart_2).properties(**PROPERTIES).resolve_scale(color='independent')