Search code examples
pythonpandasaltair

How to make the mark_rule in Altair change based on user input?


I would like to make the mark_rule (significance level) to be adjustable. I have tried to do it using user input code and change the value in the rule from 0.05 to 'user input' but the chart turned out weird.

There are two things that I would like to ask for help with:

  1. Make the mark_rule change through user input (top priority)
  2. Make the color of the bars (factors) below the mark_rule change (optional)

I have tried many codes in this, by far, I can only make the mark_rule move using mouseover but it is not exactly what I want to do.

Any help would be very much appreciated.

import pandas as pd
import altair as alt

Sheet2 = 'P-value'
df = pd.read_excel('Life Expectancy Data- Clean.xlsx', sheet_name=Sheet2)

highlight = alt.selection(type='single', on='mouseover',
                          fields=['Factor'], nearest=True, empty="none")

bar = alt.Chart(df).mark_bar(strokeWidth=5, stroke="steelblue", strokeOpacity=0.1).encode(
        x = alt.X('Factor:O', sort='y'),
        y = alt.Y('P-value:Q'),
        tooltip = [alt.Tooltip('Factor:O'),alt.Tooltip('P-value:Q',format='.4f')],
        color= alt.condition(
            highlight,
            alt.value("orange"),
            alt.value("steelblue"))
    ).add_selection(
        highlight
    )

rule = alt.Chart(pd.DataFrame({'y': [0.05]})).mark_rule(color='red').encode(y='y')

alt.layer(
    bar, rule
).properties(
    title='Factors that Contribute to Life Expectancy in Malaysia',
    width=500, height=300
)

Current graph


Solution

  • Building upon the example in the Altair docs, you could do something like this, which gives you a slider that controls the position of the rule and highlights the bars in different colors depending on if they are above or below the slider value:

    import altair as alt
    import pandas as pd
    import numpy as np
    
    
    rand = np.random.RandomState(42)
    
    df = pd.DataFrame({
        'xval': range(10),
        'yval': rand.randn(10).cumsum()
    })
    
    slider = alt.binding_range(min=0, max=5, step=0.5, name='cutoff:')
    selector = alt.selection_single(name="SelectorName", bind=slider, init={'cutoff': 2.5})
    
    rule = alt.Chart().mark_rule().transform_calculate(
        rule='SelectorName.cutoff'
    ).encode(
        # Take the mean to avoid creating multiple lines on top of eachother
        y='mean(rule):Q', 
    )
    
    bars = alt.Chart(df).mark_bar().encode(
        x='xval:O',
        y='yval',
        color=alt.condition(
            alt.datum.yval < selector.cutoff,
            alt.value('coral'), alt.value('steelblue')
        )
    ).add_selection(
        selector
    )
    
    bars + rule
    

    enter image description here