I’m not sure if this has been figured out and posted elsewhere, but I’ve looked for a solution and haven’t found exactly what I was looking for.
I’ve been building a dashboard for drilling down into data and I wanted to sync the options for all the multiselect components such that you don’t see options that aren’t available. Meaning, when you select value from one multiselect, the options for the other multiselects update.
I found this post: Dynamic multiselect options tied to presence in a list/dataframe, which handles syncing options, but only downstream.
I made a solution that seems to work pretty well. It’s not exactly simple, but it does what I need to do.
Basically, you store each component’s value in session state and use that to generate new options each time the app refreshes.
import streamlit as st
import pandas as pd
from vega_datasets import data
df = data.cars()
df['Year'] = df['Year'].dt.year
# The components like to update and choose new values when you apply these methods.
# Each time the dashboard updates, you need to set the reset the value and options for each component.
# To do that, store each components value in st.session_state.
# if there is a value in st.session_state for years
if "years" in st.session_state and st.session_state['years']:
# get the selected value. Use this to set the value of the year slider later
years = st.session_state['years']
# filter the dataframe for the selected years and grab the indices
year_idx = df[(df['Year'] >= years[0]) & (df['Year'] <= years[1])].index
# if no value is selected or "years" hasn't been added to the session_state yet, set default values
else:
years = [min(df['Year']), max(df['Year'])]
year_idx = df['Year'].index
# Do the same for names
if 'name' in st.session_state and st.session_state['name']:
names = st.session_state['name']
name_value = names
name_idx = df[df['Name'].isin(names)].index.unique()
else:
name_value = []
name_idx = df.index
# Do the same for Origin
if 'origin' in st.session_state and st.session_state['origin']:
origins = st.session_state['origin']
origin_value = origins
origin_idx = df[df['Origin'].isin(origins)].index.unique()
else:
origin_value = []
origin_idx = df.index
# Find the interseciton of all the filtered indices.
idx = list(set(year_idx).intersection(set(name_idx), set(origin_idx)))
# Filter the dataframe
dff = df.loc[idx]
# Make the year slider
years = st.slider("Years", min_value=min(df['Year']), max_value=max(df['Year']), value=years, key="years")
# Get the filtered origin options
origin_options = df.loc[list(set(year_idx).intersection(set(name_idx)))]['Origin'].sort_values().unique()
# Make the origin multiselect
origins = st.multiselect("Origin", origin_options, default=origin_value, key="origin")
# Show the number of Origin options (just sanity check that it updated)
st.write(len(dff['Origin'].unique()))
# Get the filtered name options
name_options = df.loc[list(set(year_idx).intersection(set(origin_idx)))]['Name'].sort_values().unique()
# Make the name multiselect
names = st.multiselect("Name", name_options, default=name_value, key="name")
# Show the number of Name options (just sanity check that it updated)
st.write(len(dff['Name'].unique()))
# Show the filtered dataframe
st.write(dff)
I don’t know if this is the easiest or fastest way to do this, but it’s a solution that seems to work. It’s a bit cumbersome if you have a lot of components you want synced, but worth it in my opinion.