A simple answer would be to include a switch for which one you want selected first, so that you can use exactly your solution, but with two versions depending on which one a user wants first.
first = st.radio('Select first:',['a','b'])
if first == 'a':
# lay out the buttons with a first
else:
# lay out the buttons with b first
A more complicated solution is to build in some interactivity between the buttons. Here’s an example. However, note that since each button updates relative to action on the other, you can get yourself stuck in a dead end, hence the reset button. It starts by showing all data, and as you remove values, it makes sure to remove options (and selections if affected) accordingly. I can think of other ways to implement this depending on how much back-and-forth you want possible, so this more to show how you can get widgets talking to each other in a bidirectional manner using session_state and callback functions.
import streamlit as st
import pandas as pd
# page prefix
pre = 'interacting_widget_options__'
# column selection
col1 = 'fur'
col2 = 'color'
@st.experimental_memo
def get_data():
data = pd.DataFrame({'fur':['DSH','DSH','DSH','DSH','DSH','DMH',
'DMH','DMH','DLH','DLH','DLH','DLH'],
'sex':['M','F','M','M','F','M',
'M','F','F','F','F','M'],
'color':['White','Black','Orange','Orange','Calico','Orange',
'Black','Brown','Calico','Black','White','Brown']})
return data
df = get_data()
def initialize():
if (pre+col1+'_possible') not in st.session_state or \
(pre+col2+'_possible') not in st.session_state:
st.session_state[pre+col1+'_possible'] = df[col1].unique()
st.session_state[pre+col2+'_possible'] = df[col2].unique()
initialize()
def reset():
st.session_state.clear()
return
st.button('Reset',on_click=reset)
def get_row_values(data, col1, filter1, options1, col2, filter2, options2):
filter1_list = st.session_state[filter1]
filter2_list = st.session_state[filter2]
filtered_data = data[data[col1].isin(filter1_list)]
available_col2_values = filtered_data[col2].unique()
st.session_state[filter2] = [selected for selected in filter2_list \
if selected in available_col2_values]
st.session_state[options2] = available_col2_values
filter2_list = st.session_state[filter2]
filtered_data = data[data[col2].isin(filter2_list)]
available_col1_values = filtered_data[col1].unique()
st.session_state[options1] = available_col1_values
st.session_state[filter1] = filter1_list
return
filter = {}
filter[col1] = st.multiselect(f'Select {col1}',st.session_state[pre+col1+'_possible'],
key=(pre+col1+'_selected'),
default = st.session_state[pre+col1+'_possible'],
on_change = get_row_values,
args=(df,col1,(pre+col1+'_selected'),(pre+col1+'_possible'),
col2,(pre+col2+'_selected'),(pre+col2+'_possible')))
filter[col2] = st.multiselect(f'Select {col2}',st.session_state[pre+col2+'_possible'],
key=(pre+col2+'_selected'),
default = st.session_state[pre+col2+'_possible'],
on_change = get_row_values,
args=(df,col2,pre+col2+'_selected',pre+col2+'_possible',
col1,pre+col1+'_selected',pre+col1+'_possible'))
df[df[col1].isin(filter[col1])][df[col2].isin(filter[col2])]
st.write(pd.DataFrame({'key':st.session_state.keys(),'value':st.session_state.values()}))
Click here for a demo hosted on Streamlit cloud
(edit to change app url)