How to change color of a bar based on dataframe values?

Hi everyone,

I hope that I’m right here in this section asking for help. If not, then please apologize this post.

I try to create a bar chart having products on the Y axis and their delay on the X axis. Any product exceeding its defined maximum delay should be shown in a different color (i.e. red), while everything else may be shown in green color.

What I already got is a bar chart, sorted by their overdue values but I’m not able to apply a different color on specific bars.

Here’s the current dataframe df_to_display:

      PRODUKT  CURRENT_DELAY  OVERDUE
6           C            546    False
5           A             76    False
17          X             27     True
12          T             21     True
11          S              3     True
3           M              3     True
14          V              3    False
7           R              3     True

Those products having OVERDUE == True should be displayed with a red bar, all others in green.

The code for the bar chart I’ve got so far is this:

st.altair_chart(
    alt.Chart(df_to_display)
    .mark_bar()
    .encode(
        x=alt.X("CURRENT_DELAY", axis=alt.Axis(title="Current Delay (days)")),
        y=alt.Y(
            "PRODUKT",
            axis=alt.Axis(title="Product"),
            sort=alt.EncodingSortField(field="CURRENT_DELAY", order="descending"),
        ),
    ),
    use_container_width=True,
)

Does anyone have a tip for me on how to do that?

I’m using:
Streamlit 1.41.1
Altair 5.5.0
Python 3.9

Many thanks in advance!

Hey @mosesontheweb ,

Here’s an alternative to this approach - Defining Altair colour scales first and then building the chart.

For example -

# Define color mapping
color_scale = alt.condition(
    alt.datum.OVERDUE,  # Condition: If OVERDUE is True
    alt.value("red"),    # Red for True (overdue)
    alt.value("green"),  # Green for False (not overdue)
)

# Create Altair bar chart
chart = (
    alt.Chart(df_to_display)
    .mark_bar()
    .encode(
        x=alt.X("CURRENT_DELAY:Q", axis=alt.Axis(title="Current Delay (days)")),
        y=alt.Y(
            "PRODUKT:N",
            axis=alt.Axis(title="Product"),
            sort=alt.EncodingSortField(field="CURRENT_DELAY", order="descending"),
        ),
        color=color_scale,  # Apply color based on OVERDUE
    )
)

# Display the chart in Streamlit
st.altair_chart(chart, use_container_width=True)

Hope this helps. :slight_smile:

Hi @Dhruv4 ,

thanks for your efforts, I’ll give it a try for sure. Five minutes ago I discovered a working example as well, using the examples on Altair’s website.

Here’s my approach (quite similar to yours):

color = alt.when(OVERDUE=True).then(alt.value('red')).otherwise(alt.value('steelblue'))

st.altair_chart(
    alt.Chart(df_to_display)
    .mark_bar()
    .encode(
        x=alt.X("CURRENT_DELAY", axis=alt.Axis(title="Current Delay (days)")),
        y=alt.Y(
            "PRODUKT",
            axis=alt.Axis(title="Product"),
            sort=alt.EncodingSortField(field="CURRENT_DELAY", order="descending"),
        ),
        color=color,
    ),
    use_container_width=True,
)

What’s puzzling me is the usage of alt.datum.OVERDUE - the term datum means date in English, so it’s not easy to understand why I find OVERDUE in alt.datum. :slight_smile:

Anyway, a big thank you from my side!

1 Like

Not really-

Well, I should have been more precise. Datum in German means Date in English. :slight_smile: And since I’m Austrian, this really confused me.

2 Likes