footfalcon
footfalcon

Reputation: 631

How to set multiple conditions for color in Altair 5.5 using alt.when().then().otherwise()?

How can I set multiple conditions for colours of a bar chart? Specifically, I want the color scheme for values < 0 to be "reds", values > 0 to "lightgray" and the "Gold" name to be "goldenrod" color. I am using Altair 5.5.

Here is what I have tried:

np.random.seed(1)
test = pd.DataFrame(
    {
        'Name': ['Cat_A', 'Cat_B', 'Cat_C', 'Cat_D', 'Gold'],
        'Value': np.random.randint(-10,10,5),
    },
).sort_values(by='Value', ascending=False).reset_index(drop=True).reset_index()
test.rename(columns={'index': 'Rank'}, inplace=True)

color = (
    alt.when(alt.datum.Value < 0)           # check if Value < 0
    .then(alt.Color("Rank:O").scale(scheme='reds').legend(None)) # then 'reds'
    #.when(alt.datum.Name == 'Gold')        # check if Name is 'Gold'
    #.then(alt.value('goldenrod'))          # If true, use 'goldenrod'
    .otherwise(alt.value('lightgray'))      # Default color if neither is true
)
alt.Chart(test).mark_bar(tooltip=True).encode(
    x=alt.X('Value:Q').title('Change in Score'),
    y=alt.Y('Name:N', sort=test['Name'].to_list()).title(None),
    color=color
)

I can only get two conditions to work, and not the "goldenrod" color (which I have commented out above in order to get this):

enter image description here

Upvotes: 1

Views: 50

Answers (1)

fitzme
fitzme

Reputation: 292

I believe the basic usage of chaining when().then() multiple times is that it implies a priority: i.e. the FIRST when() that is true will take effect.

In the example shown here, "Gold" has a value < 0, and therefore (were it not commented) it would still trigger the first when() and be colored with the Reds scale, and this plot would look the same as it does now.

On the other hand, if name=="Gold" was the first when().then(), I think you would get the result you want.

-- UPDATE --

I'm just coming back to Altair ver 5+, and it seems the new when().then() syntax does not like mixing in alt.Color() encodings, and returns an error for me "TypeError: Chained conditions cannot be mixed with field conditions."

So instead, here's an old school hacky way of doing it. Still works, I tested it this time:

condition1 = alt.condition(alt.datum.Name == 'Gold', alt.value('goldenrod'), alt.Color("Rank:O").scale(scheme='reds').legend(None))
condition2 = alt.condition(alt.datum.Value >= 0, alt.value('lightgray'), alt.Color("Rank:O").scale(scheme='reds').legend(None))
combined_condition = condition1.copy()
combined_condition['condition'] = [condition1['condition'], condition2['condition']]


alt.Chart(test).mark_bar(tooltip=True).encode(
    x=alt.X('Value:Q').title('Change in Score'),
    y=alt.Y('Name:N', sort=test['Name'].to_list()).title(None),
    color=combined_condition
)

bar chart

Is cobbling together two alt.condition()s elegant? Nope, but I hope this helps. It might break with small variations because I don't know if this hack is intended, but it works for this example.

Upvotes: 0

Related Questions