altair icon indicating copy to clipboard operation
altair copied to clipboard

[Doc] Update Bar Chart Hightlighted Segment: 2 Thresholds

Open ChiaLingWeng opened this issue 2 years ago • 2 comments

This will close #2996 and related to #1109 Add another highlighted sections and label text to show more flexibility image

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

ChiaLingWeng avatar Oct 13 '23 06:10 ChiaLingWeng

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'])

image

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

mattijn avatar Oct 13 '23 07:10 mattijn

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

ChiaLingWeng avatar Oct 16 '23 00:10 ChiaLingWeng