Streamlit:更新选项多选

huangapple go评论74阅读模式
英文:

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)

Streamlit:更新选项多选

huangapple
  • 本文由 发表于 2023年5月15日 01:21:26
  • 转载请务必保留本文链接:https://go.coder-hub.com/76248797.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定