Search code examples
pythonaltairvega-literidgeline-plot

Different colors in Altair Ridgeline plot


It's an extension to my previous question about the Altair Ridgeline plot.

I have a plot which created like this:

import pandas as np
import numpy as np

source = pd.DataFrame(columns=list('ab'))
source['a'] = np.random.randint(0,17,size=500)
source['color'] = source['a'].apply(lambda x: 'blue' if x < 10 else 'red'] 
source['a'] = source['a'].astype('str')
source['b'] = np.random.randint(1000,5000,size=500).astype('float')


import altair as alt

step = 20
overlap = 1

alt.Chart(source, height=step).transform_joinaggregate(
    mean_temp='mean(b)', groupby=['a']
).transform_bin(
    ['bin_max', 'bin_min'], 'b'
).transform_aggregate(
    value='count()', groupby=['a', 'b', 'bin_min', 'bin_max']
).transform_impute(
    impute='value', groupby=['a', 'b'], key='bin_min', value=0
).mark_area(
    interpolate='monotone',
    fillOpacity=0.8,
    stroke='lightgray',
    strokeWidth=0.5
).encode(
    alt.X('bin_min:Q', bin='binned', title=''),
    alt.Y(
        'value:Q',
        scale=alt.Scale(range=[step, -step * overlap]),
        axis=None
    ),
    alt.Fill(
        'b:Q',
        legend=None,
    )
).facet(
    row=alt.Row(
        'a:N',
        title=None,
        header=alt.Header(labelAngle=0, labelAlign='right')
    )
).properties(
    title='',
    bounds='flush'
).configure_facet(
    spacing=0
).configure_view(
    stroke=None
).configure_title(
    anchor='end'
)

My question is how to make rows of the plot different colors ('blue' or 'red' depending on 'color' column in dataframe)? I tried to configure it using alt.Scale(domain='color:N') in alt.Fill(), color='color:N' param in encode() but it wouldn't work. Facet header labels should be colored as well.


Solution

  • You can do this by setting the fill or color encoding to the "color" column with a raw scale.

    That said, you have already set the color encoding based on the "b" column, so you need to encode this information differently; for example, you could use Opacity instead.

    Here is an example of putting these together:

    import pandas as np
    import numpy as np
    
    source = pd.DataFrame(columns=list('ab'))
    source['a'] = np.random.randint(0,17,size=500)
    source['color'] = source['a'].apply(lambda x: 'blue' if x < 10 else 'red')
    source['a'] = source['a'].astype('str')
    source['b'] = np.random.randint(1000,5000,size=500).astype('float')
    
    
    import altair as alt
    
    step = 20
    overlap = 1
    
    alt.Chart(source, height=step).transform_joinaggregate(
        mean_temp='mean(b)', groupby=['a']
    ).transform_bin(
        ['bin_max', 'bin_min'], 'b'
    ).transform_aggregate(
        value='count()', groupby=['a', 'b', 'bin_min', 'bin_max', 'color']
    ).transform_impute(
        impute='value', groupby=['a', 'b', 'color'], key='bin_min', value=0
    ).mark_area(
        interpolate='monotone',
        fillOpacity=0.8,
        stroke='lightgray',
        strokeWidth=0.5
    ).encode(
        alt.X('bin_min:Q', bin='binned', title=''),
        alt.Y(
            'value:Q',
            scale=alt.Scale(range=[step, -step * overlap]),
            axis=None
        ),
        alt.Fill('color:N', scale=None),
        alt.Opacity(
            'b:Q',
            legend=None,
        )
    ).facet(
        row=alt.Row(
            'a:N',
            title=None,
            header=alt.Header(labelAngle=0, labelAlign='right')
        )
    ).properties(
        title='',
        bounds='flush'
    ).configure_facet(
        spacing=0
    ).configure_view(
        stroke=None
    ).configure_title(
        anchor='end'
    )
    

    enter image description here