Interactive bar plot with multiple dataframe column selection

129 Views Asked by At

TLDR: I want to create an interactive bar graph in Python where I can select which bars appear on the plot by toggling values from multiple categorical dataframe columns.

The data

I have a dataframe with an ID column x, an output data column y, and three categorical columns used to classify each datapoint as big or small, A or B, and blue or red.

import pandas as pd

data = dict(size=['big', 'big', 'big', 'big', 'small', 'small', 'small', 'small'],
            design=['A', 'A', 'B', 'B', 'A', 'A', 'B', 'B'],
            colors=['blue', 'red', 'blue', 'red', 'blue', 'red', 'blue', 'red'],
            x=['1', '2', '3', '4', '5', '6', '7', '8'],
            y=[10, 20, 10, 30, 10, 40, 10, 30])
data = pd.DataFrame(data)
print(data)

Output:

    size design colors  x   y
0    big      A   blue  1  10
1    big      A    red  2  20
2    big      B   blue  3  10
3    big      B    red  4  30
4  small      A   blue  5  10
5  small      A    red  6  40
6  small      B   blue  7  10
7  small      B    red  8  30

What I have so far

Using Altair and the code below, I'm able to make the data selectable like I want using three separate interactive legends, but I am unable to get the three legends to cooperate. That is to say, when you make a selection using one of the legends, that selection is not necessarily preserved when interacting with the other legends.

import altair as alt

selection = alt.selection_multi(encodings=['x'])
color = alt.condition(selection,
                        alt.Color(legend=None),
                        alt.value('lightgray'))


selection_chart = alt.Chart(data).mark_bar().encode(
    x=alt.X('x',
            axis=alt.Axis(title='ID Number')), 
    y=alt.Y('y',
            axis=alt.Axis(title='Output')),
    color=color
).interactive()

legend1 = alt.Chart(data).mark_rect().encode(
    x='size:O',
    color=color
).add_selection(
    selection
)

legend2 = alt.Chart(data).mark_rect().encode(
    x='design:O',
    color=color
).add_selection(
    selection
)

legend3 = alt.Chart(data).mark_rect().encode(
    x='colors:O',
    color=color
).add_selection(
    selection
)

legend = alt.Chart()

selection_chart | (legend1 & legend2 & legend3)

The above code outputs an interactive plot, an image of which is shown below.

Image of the interactive plot generated by the above code

The question

Is there a way I can generate an interactive plot where I can reliably toggle size, design and colors? I feel like I'm close, but the strange, uncooperative behavior of the multiple interactive legends is a huge issue.

1

There are 1 best solutions below

0
joelostblom On

You can use selector composition with logical operators such as &, for example:

selection1 = alt.selection_multi(fields=['size'])
color1 = alt.condition(
    selection1,
    alt.Color(legend=None),
    alt.value('lightgray')
)
legend1 = alt.Chart(data).mark_rect().encode(
    x='size:O',
    color=color1
).add_selection(
    selection1
)

selection2 = alt.selection_multi(fields=['design'])
color2 = alt.condition(
    selection2,
    alt.Color(legend=None),
    alt.value('lightgray')
)
legend2 = alt.Chart(data).mark_rect().encode(
    x='design:O',
    color=color2
).add_selection(
    selection2
)

color = alt.condition(
    selection1 & selection2,
    alt.Color(legend=None),
    alt.value('lightgray')
)
selection_chart = alt.Chart(data).mark_circle().encode(
    x=alt.X('x'), 
    y=alt.Y('design'),
    size=alt.Size('size', legend=None, sort=['small', 'big']),
    color=color
)

selection_chart | (legend1 & legend2)

enter image description here