Session state with pandas - editing a value

Hi all,

I’m using streamlit 0.84.1.

My project loads a dataframe with a unique title on each row, and the user is allowed to modify the “subscribed” status of each row (all set to FALSE in the example below):

Currently when the “subscribed” status gets changed, rerunning the app from top to bottom clears those edits and resets everything. So I’ll need to use session state to preserve those changes across re-runs and not lose that information.

However, I can’t figure out how to dynamically add to session state. The documentation example of using a counter worked because the counter variable always had the same name. Here, I think I want my [key] to be the title, and [value] to be the new “subscribed” state. (Users can change the “subscribed” status of the same title multiple times, but I haven’t figured out how to track that yet.)

if 'Developmental Biology' not in st.session_state:
    st.session_state['Developmental Biology'] = 'TRUE'

So, in my MWE below, I need some advice on how to proceed within the if st.button block. How do I get title to show as different names each time through the loop, such as Developmental Biology?

import streamlit as st
import pandas as pd

@st.cache(allow_output_mutation=True, suppress_st_warning=True)
def load_data(file):
    st.write("New file loaded")
    return pd.read_csv(file)#, sep=',', encoding='utf-8')  #Process the data in cached way to speed up processing

#load data
file = "test_10lines.csv"
df = load_data(file)

#change the Boolean TRUE/FALSE to simply uppercase strings
df['subscribed'] = df['subscribed'].astype(str)
df['subscribed'] = df['subscribed'].str.upper()
df

#offer user the journal titles, and they select one or multiple to edit at a time
selected_titles = st.multiselect('Journal Name:', pd.Series(df['title']), help='Displayed in order provided by the underlying datafile')
st.write(selected_titles)

#what to change 'subscribed' to
radiovalue = st.radio("Change 'Subscribed' status to:", ['TRUE', 'MAYBE', 'FALSE'])

if st.button('Commit change!'):
    for title in selected_titles:
        if title not in st.session_state:     #initialize counter the first time
            st.session_state.df['title'] = 'test'
        title_filter = (df['title'] == title)
        df.at[title_filter, 'subscribed'] = radiovalue

df

Have you considered using the AgGrid component, which you can set up to allow users to edit cells directly?

I did not know about AgGrid, thank you!

I think I came up with a solution. I made four separate lists, one each to store titles the user wants to change to TRUE, FALSE, MAYBE, and (blank).

# Initialize session_state versions of to_true/false/maybe/blank lists
if 'to_true' not in st.session_state:
    st.session_state.to_true = []
if 'to_false' not in st.session_state:
    st.session_state.to_false = []
if 'to_maybe' not in st.session_state:
    st.session_state.to_maybe = []
if 'to_blank' not in st.session_state:
    st.session_state.to_blank = []

When user commits the change to a title, I use a custom function to remove the title from all four lists so I only keep the most recent change for that title.

Then I add the title to whichever list matches what the user wanted.

            if st.button('Commit change!'):
                for title in selected_titles:
                    clear_title_from_list(title)  #remove title from all lists, so we only keep the most recent change

                if radiovalue == 'TRUE':
                    for title in selected_titles:
                        st.session_state.to_true.append(title)
                        
                if radiovalue == 'FALSE':
                    for title in selected_titles:
                        st.session_state.to_false.append(title)
                        
                if radiovalue == 'MAYBE':
                    for title in selected_titles:
                        st.session_state.to_maybe.append(title)
                        
                if radiovalue == " ":
                    for title in selected_titles:
                        st.session_state.to_blank.append(title)

Finally, I actually execute the changes. This runs every time the script runs but session_state lets me save the previous changes.

for title in st.session_state.to_true:
    title_filter = (df['title'] == title)
    df.at[title_filter, 'subscribed'] = 'TRUE'

for title in st.session_state.to_false:
    title_filter = (df['title'] == title)
    df.at[title_filter, 'subscribed'] = 'FALSE'
    
for title in st.session_state.to_maybe:
    title_filter = (df['title'] == title)
    df.at[title_filter, 'subscribed'] = 'MAYBE'
    
for title in st.session_state.to_blank:
    title_filter = (df['title'] == title)
    df.at[title_filter, 'subscribed'] = ' '