英文:
Streamlit : update options multiselect
问题
I understand your request. Here's the translated code portion:
我有一个包含3列的数据框,假设它们是['动物类型','品种','颜色']。
我已经为这3列创建了3个多选过滤器。
现在,当我更改一个过滤器时,我希望其他过滤器的选项也被更新。
例如:
| 动物类型 | 品种 | 颜色 |
| -------- | -------- |-------- |
| 狗 | 波尔多犬 | 棕色 |
| 狗 | X | 黑色 |
| 狗 | X | 黑色 |
| 狗 | 阿斯基狗 | 绿色 |
| 猫 | Y | 黄色 |
| 猫 | Y | 棕色 |
| 蜘蛛 | 阿斯基狗 | 白色 |
| 蜘蛛 | 阿斯基狗 | 黑色 |
如果我在多选中选择了选项['动物类型'] = ['狗'],我希望将可能的颜色选项更新为['棕色','黑色','绿色']。
当我在多选中选择了选项['颜色'] = ['黑色']时,我希望将可能的动物类型选项更新为['狗','蜘蛛']。
Please note that I've translated the code comments and variable names into Chinese, but the code logic remains the same.
英文:
I have a dataframe with 3 columns, let's say they are ['animal type','breed','colour'].
I have created 3 multi select filters for the 3 columns.
Now, when I change one filter, I want the options of the other filters to be updated.
Ex:
Animal type | Breed | Colour |
---|---|---|
Dog | bulldog | brown |
Dog | X | Black |
Dog | X | Black |
Dog | asky | Green |
Cat | Y | Yellow |
Cat | Y | Brown |
Spider | asky | White |
Spider | asky | Black |
If I select the option['Animal Type']=['Dog'] in a multiselect, I want to update the possible colour options to ['Brown','Black','Green'].
When I select the option['Colour']=['Black'] in a multiselect, I want to update the possible animal type options to ['Dog','Spider'].
I have tried this, but it only updates when I filter by animal type.
ANIMAL = list(set(df['Animal Type']))
BREED = list(set(df['Breed']))
COLOR = list(set(df['Colour']))
options1= st.sidebar.multiselect('Animal Type',ANIMAL)
if options1:
options2 = st.sidebar.multiselect('Select BREED',set(df[df['Animal Type'].isin(options1)]['Breed']))
options3 = st.sidebar.multiselect('Select COLOR',set(df[df['Breed'].isin(options2)]['Color']))
else:
options2 = st.sidebar.multiselect('Select BREED',BREED)
options3 = st.sidebar.multiselect('Select COLOR',COLOR)
Does anyone know how to update all option filters, after a filtering?
答案1
得分: 0
这基本上是评论中提到的相同解决方案。但是,用户选择过滤器顺序的机制已经更改。这会创建额外的代码,因此逻辑会变得有点复杂。
正如在另一个解决方案中提到的,如果要允许用户在过滤器之间来回切换以进行选择,则应该建立某种“过滤顺序”。在下面的实现中,提供了三个过滤器,并且随着进行选择,会话状态值会跟踪最后编辑的内容,并将其移动到有序的“已确认”列表中,用户继续编辑下一个过滤器。如果当前正在编辑的过滤器被清除,将撤消最后的确认。
英文:
This is basically the same solution mentioned in the comments. However, the mechanism by which a user chooses the order of filters has been changed. This creates extra code, so the logic is a bit more convoluted.
As mentioned in the other solution, there is a lot more complexity if you want to allow a user to go back and forth between filters to make selections, as such some kind of "order of filtering" should be established. In the implementation below, three filters are presented, and as a selection is made, session state values track what was last edited and moves things into an ordered "confirmed" list as the user moves on to edit the next filter. If the currently-being-edited filter is cleared, the last confirmation will be undone.
import streamlit as st
import pandas as pd
if 'df' not in st.session_state:
df = pd.DataFrame({
'Animal':['Dog','Dog','Dog','Dog','Cat','Cat','Spider','Spider'],
'Breed':['bulldog','X','X','asky','Y','Y','asky','asky'],
'Color':['Brown','Black','Black','Green','Yellow','Brown','White','Black']
})
st.session_state.df = df
df = st.session_state.df
df_filtered = df.copy()
# Session state values to track the order of filter selection
if 'confirmed' not in st.session_state:
st.session_state.confirmed = []
st.session_state.last_edited = None
def last_edited(i,col):
'''Update session state values to track order of editing
i:int
index of the column that was last edited
col:str
name of the column that was last edited
'''
if st.session_state.last_edited is None: # Nothing was previously selected/edited
st.session_state.last_edited = (i,col)
return
if st.session_state.last_edited == (i,col): # The same column was last edited
undo(col)
return
# Some other column was last edited:
confirmed(*st.session_state.last_edited)
st.session_state.last_edited = (i,col)
return
def undo(col):
'''Undoes the last confirmation if the last edit was to clear a filter
col : str
name of the column that was last edited
'''
if st.session_state['col_'+col] == []: # Check state of widget by key
last_confirmed = safe_pop(st.session_state.confirmed,-1)
st.session_state.last_edited = last_confirmed
def safe_pop(lst, i):
'''Pops the ith element of a list, returning None if the index is out of bounds
lst : list
list to pop from
i : int
index to pop
'''
try:
return lst.pop(i)
except IndexError:
return None
def confirmed(i,col):
'''Adds the last edited column to the confirmed list
i:int
index of the column that was last edited
col:str
name of the column that was last edited
'''
st.session_state.confirmed.append((i,col))
# Columns to display the filters (Streamlit with create multiselect widgets
# according to the order of user edits, but columns will keep them displaying
# in their original order for the user)
cols = st.columns(3)
selected = dict(zip(df.columns, [[],[],[]]))
# Confirmed filters
for i,col in st.session_state.confirmed:
selected[col] = cols[i].multiselect(
col, df_filtered[col].unique(), key=f'col_{col}',
on_change=last_edited, args=[i,col], disabled=True
)
df_filtered = df_filtered[df_filtered[col].isin(selected[col])]
#Currently editing
if st.session_state.last_edited is not None:
i,col = st.session_state.last_edited
selected[col] = cols[i].multiselect(
col, df_filtered[col].unique(), key=f'col_{col}',
on_change=last_edited, args=[i,col]
)
df_filtered = df_filtered[df_filtered[col].isin(selected[col])]
# Not yet edited filters
for i, col in enumerate(df_filtered.columns):
if (i,col) not in st.session_state.confirmed and (i,col) != st.session_state.last_edited:
selected[col] = cols[i].multiselect(
col, df_filtered[col].unique(), key=f'col_{col}',
on_change=last_edited, args=[i,col]
)
if selected[col] != []:
df_filtered = df_filtered[df_filtered[col].isin(selected[col])]
cols = st.columns(2)
cols[0].write(df)
cols[1].write(df_filtered)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论