Altair Color binned values

Viewed 664

Having trouble coloring binned values in the following histogram. I intend on coloring all bars that are less-than 50 on the x-axis (Creditworthiness). How is this done in Altair?

base = alt.Chart(X_train)

histogram = base.mark_bar().encode(
    alt.X('Creditworthiness', bin=True),
    y='count()',
    color=alt.condition(
        alt.datum.Creditworthiness < 50,
        alt.value("steelblue"),  # The positive color
        alt.value("orange")  # The negative color
    )
)

threshold_line = pd.DataFrame([{"threshold": max_profit_threshold}])
mark = alt.Chart(threshold_line).mark_rule(color="#e45755").encode(
    x='threshold:Q',
    size=alt.value(2)
)

histogram + mark

enter image description here

2 Answers

There are two ways to do this; the quick way which is undocumented and may not work in the future, and the more robust way that is a bit more code.

The quick way relies on using the internal field names generated by vega for binned encodings:

import altair as alt
import pandas as pd
import numpy as np

np.random.seed(1701)
X_train = pd.DataFrame({
    'Creditworthiness': np.clip(50 + 20 * np.random.randn(300), 0, 100)
})

alt.Chart(X_train).mark_bar().encode(
    alt.X('Creditworthiness', bin=True),
    y='count()',
    color=alt.condition(
        alt.datum.bin_maxbins_10_Creditworthiness_end <= 50,
        alt.value("steelblue"),  # The positive color
        alt.value("orange")  # The negative color
    )
)

enter image description here

The documented way is to move your binning from the encoding to an explicit transform, which is a bit more verbose:

alt.Chart(X_train).transform_bin(
    'Creditworthiness_bin', 'Creditworthiness', bin=alt.Bin(step=10)
).transform_joinaggregate(
    count='count()', groupby=['Creditworthiness_bin']  
).mark_bar(orient='vertical').encode(
    alt.X('Creditworthiness_bin:Q', bin='binned'),
    alt.X2('Creditworthiness_bin_end'),
    alt.Y('count:Q'),
    color=alt.condition(
        alt.datum.Creditworthiness_bin_end <= 50,
        alt.value("steelblue"),  # The positive color
        alt.value("orange")  # The negative color
    )
)

enter image description here

Here is a both non-verbose and robust way using documented Altair parameters. This works by binning the color encoding (using the given threshold as bin step), while restricting the color scale (colors are taken from the "category10" categorical color scheme).

import altair as alt
import pandas as pd
import numpy as np

np.random.seed(1701)
X_train = pd.DataFrame({
    'Creditworthiness': np.clip(50 + 20 * np.random.randn(300), 0, 100)
})
threshold = 50

alt.Chart(X_train).mark_bar().encode(
    alt.X('Creditworthiness', bin=True),
    y='count()',
    color=alt.Color('Creditworthiness', bin=alt.Bin(step=threshold), scale=alt.Scale(domain=[0, threshold], range=["#1f77b4", "#ff7f0e"]), legend=None),
)

Binned histogram of credit worthiness (blue below threshold of 50, and orange above)

Related