altair
altair copied to clipboard
[Doc] Update Bar Chart Hightlighted Segment: 2 Thresholds
This will close #2996 and related to #1109
Add another highlighted sections and label text to show more flexibility
import altair as alt
import pandas as pd
from vega_datasets import data
source = data.wheat()
threshold = pd.DataFrame(
data={"threshold_value": [20, 60], "threshold_label": ["> 20", "> 60"]}
)
bars = alt.Chart(source).mark_bar(color="#9CC8E2").encode(
x="year:O",
y=alt.Y("wheat:Q",title="Wheat")
)
highlight = alt.Chart(source).mark_bar(color="#5BA3CF").encode(
x='year:O',
y='baseline:Q',
y2='wheat:Q'
).transform_filter(
alt.datum.wheat > 20
).transform_calculate("baseline", "20")
highlight2 = alt.Chart(source).mark_bar(color="#2978B8").encode(
x='year:O',
y='baseline:Q',
y2='wheat:Q'
).transform_filter(
alt.datum.wheat > 60
).transform_calculate("baseline", "60")
rule = alt.Chart(threshold).mark_rule().encode(
y=alt.Y("threshold_value")
)
label = rule.mark_text(
x=alt.expr('width'), align="right",dx=20, dy=-5, fontWeight="bold", fontSize=12
).encode(
text="threshold_label",
)
(bars + highlight + highlight2 + rule +label).properties(width=600)
One thing need to be improved is to use variable instead of key-in value in
transform_filter() and transform_calculate().
Use for loop like https://github.com/altair-viz/altair/pull/1109#issuecomment-1475015880 may help, but not sure if it is clear enough
Thank you for pushing this @ChiaLingWeng! I tried to add my commit as a suggestion, but that was not allowed since the change covered deleted lines. With this suggestion we do not need the transform_calculate, so that is one step closer. I'm not sure if we can simplify this more, but maybe this change gives you another idea.
Explaining the change, instead of doing this:
highlight = alt.Chart(source).mark_bar(color="#5BA3CF").encode(
x='year:O',
y='baseline:Q',
y2='wheat:Q'
).transform_filter(
alt.datum.wheat > 20
).transform_calculate(
"baseline", "20"
)
We can write it as such:
highlight = alt.Chart(source).mark_bar(color="#5BA3CF").encode(
x='year:O',
y='wheat:Q',
y2=alt.datum(20)
).transform_filter(
alt.datum.wheat > 20
)
Observe using wheat:Q for the y encoding channel instead of using the y2 channel.
Thinking more, we might be able to use a layer-repeat operator here. This works:
alt.Chart(source, width=600).mark_bar().encode(
x='year:O',
y='wheat:Q',
y2=alt.Y2Datum(alt.repeat('layer')),
color=alt.ColorDatum(alt.repeat('layer'))
).repeat(layer=['0','20', '60'])
But adding a repeat operator to a filter transform does not work
alt.Chart(source, width=600).mark_bar().encode(
x='year:O',
y='wheat:Q',
y2=alt.Y2Datum(alt.repeat('layer')),
color=alt.ColorDatum(alt.repeat('layer'))
).transform_filter(
alt.FieldGTPredicate(field='wheat', gt=alt.repeat('layer'))
).repeat(layer=['0','20', '60'])
SchemaValidationError: '{'repeat': 'layer'}' is an invalid value for `gt`. Valid values are of type 'string' or 'number'. Additional properties are not allowed ('repeat' was unexpected)'expr' is a required property
See also https://github.com/vega/vega-lite/issues/7398. The expecting VL-spec should look as such Open the Chart in the Vega Editor
Thanks for your feedback @mattijn !
I never thought to use y2 channel and layer-repeat operator here, I'll spend some time to study if I can help at the vega side