pandas Streamlit:更新选项多选

s1ag04yj  于 2023-05-21  发布在  其他
关注(0)|答案(1)|浏览(131)

我有一个3列的dataframe,假设它们是['animal type','breed','colour']。我已经为3列创建了3个多选过滤器。现在,当我更改一个过滤器时,我希望其他过滤器的选项也会更新。
例如:
| 动物类型|品种|颜色|
| --------------|--------------|--------------|
| 狗|斗牛犬|褐色|
| 狗|X|黑色|
| 狗|X|黑色|
| 狗|歪斜的|绿色|
| 猫|Y|黄色|
| 猫|Y|布朗|
| 蜘蛛|歪斜的|白色|
| 蜘蛛|歪斜的|黑色|
如果我在多选中选择了['Animal Type']=['Dog']选项,我想将可能的颜色选项更新为['Brown','Black','Green']。当我在多选中选择['Colour']=['Black']选项时,我想将可能的动物类型选项更新为['Dog','Spider']。
我试过这个,但它只更新时,我过滤的动物类型。

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)

有谁知道如何更新所有的选项过滤器,过滤后?

kqlmhetl

kqlmhetl1#

这基本上与评论中提到的解决方案相同。然而,用户选择过滤器顺序的机制已经改变。这会创建额外的代码,因此逻辑会更加复杂。
正如在另一个解决方案中提到的,如果你想允许用户在过滤器之间来回进行选择,那么就有更多的复杂性,因为应该建立某种“过滤顺序”。在下面的实现中,呈现了三个过滤器,并且当做出选择时,会话状态值跟踪上次编辑的内容,并且当用户继续编辑下一个过滤器时,将内容移动到有序的“确认”列表中。如果清除了当前正在编辑的过滤器,则将撤消上次确认。

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)

相关问题