Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Altair double dropdown menu

I want to create a plot with two dropdown menus. The opacity of the lines should depend on the selection in both dropdown menus.

Ideally the possible content of the second dropdown menu ("dropdown_symbol" below) should depend on the selection in the first dropdown (when "category_2" is selected in the first dropdown, only show "AMZM" and "IBM" in the second dropdown).

Unfortunately neither the opacities work properly nor the restriction of the possible content. Here is my sample code:

import altair as alt
from vega_datasets import data

source = data.stocks()
source.symbol.value_counts()


source["category"] = "category_1"
source.loc[source["symbol"].isin(["AMZN", "IBM"]), "category"] = "category_2"


dropdown_category = alt.binding_select(options=list(source["category"].unique()), name=" ")
dropdown_symbol = alt.binding_select(options=list(source["symbol"].unique()), name=" ")

selection_category = alt.selection_single(fields=["category"], bind=dropdown_category)
selection_symbol = alt.selection_single(fields=["symbol"], bind=dropdown_symbol)

chart = alt.Chart(source).mark_line().encode(
    x='date',
    y='price',
    color='symbol',
    opacity=alt.condition(
        selection_category & selection_symbol,
        alt.value(1),
        alt.value(0.1)
)).add_selection(selection_symbol, selection_category)

chart

enter image description here

like image 496
harry-plotter Avatar asked Sep 15 '25 02:09

harry-plotter


2 Answers

I want to create a plot with two dropdown menus. The opacity of the lines should depend on the selection in both dropdown menus.

If you want the opacity to depend on the selection within two dropdown menus, you can use an alt.condition statement and pass the two selection objects with a boolean operator; for example:

opacity=alt.condition(
        selection1 & selection2,
        alt.value(1),
        alt.value(0.1))

will choose the first value only if the point is within both selections. Alternatively,

opacity=alt.condition(
        selection1 | selection2,
        alt.value(1),
        alt.value(0.1))

will choose the first value if the point is within at least one of the selections.

Note that when using multiple selections in this way, there is a known bug around the behavior for empty selections; see https://github.com/altair-viz/altair/issues/1759 and references therein.

Ideally the possible content of the second dropdown menu ("dropdown_symbol" below) should depend on the selection in the first dropdown (when "category_2" is selected in the first dropdown, only show "AMZM" and "IBM" in the second dropdown).

It is not possible in Altair for the content of a dropdown menu to change dynamically based on the selection within another dropdown menu.

For this to change, the feature would have to be added to Vega-Lite: you can file feature requests here.

like image 174
jakevdp Avatar answered Sep 16 '25 19:09

jakevdp


I have been struggling with making something similar work, and I guess there are others like me, so let me post my solution to the first part of your problem here.

To follow up on jakevdp's answer mentioning the bug in Vega-lite: there is a workaround described here. However, I couldn't find a way to translate that to proper Altair Python code, so I opted for writing the condition as a Vega spec in Json/embedded dicts format. The key here is to include the test field.

Furthermore, you probably want a way to reset the selection. This can be done by including None among the options, as described here.

The code below works for me with Altair 4.1.0.

import altair as alt
from vega_datasets import data

source = data.stocks()
source.symbol.value_counts()

source["category"] = "category_1"
source.loc[source["symbol"].isin(["AMZN", "IBM"]), "category"] = "category_2"

cat_values = list(source["category"].unique())
sym_values = list(source["symbol"].unique())

cat_options = [None] + cat_values
sym_options = [None] + sym_values

cat_labels = ["All"] + cat_values
sym_labels = ["All"] + sym_values

dropdown_category = alt.binding_select(options=cat_options, labels=cat_labels, name=" ")
dropdown_symbol = alt.binding_select(options=sym_options, labels=sym_labels, name=" ")

selection_category = alt.selection_single(fields=["category"], bind=dropdown_category, name="cat")
selection_symbol = alt.selection_single(fields=["symbol"], bind=dropdown_symbol, name="sym")

op_condition = {"condition":
                {"test":
                 {"and":
                  [{"selection": "cat"},
                   {"selection": "sym"}]},
                    "value": 1},
                "value": 0.1}

chart = alt.Chart(source).mark_line().encode(
    x='date',
    y='price',
    color='symbol',
    opacity=op_condition
).add_selection(selection_symbol, selection_category)

chart
like image 25
emso Avatar answered Sep 16 '25 17:09

emso