I’m trying to make an app that loads a df that will later be used as input data further down the workflow. It’s quite large so the user will need to be able to apply filters and check boxes to mark what rows they want carried forward.
My problem is trying to preserve the user’s choices between changing the filters. I’ve tried implementing session states and following guides (python - Retaining changes for Streamlit data editor when hiding or switching between widgets/pages - Stack Overflow) but I’ve not been able to get it to work. When the edited df saves over the original df it also applies the filter, effectively deleting everything filtered out. Also, as I’m using dynamic_filters instead of changing page (as in the guide), I’m struggling to find a good place to add an on_change. I fell into the pitfall of having the on_change in the data_editor so it has the “have to apply all changes twice” flaw.
I’m working on fixing the filter overwrite by only updating the changed values, but I’m still new to python so I’ve not been having much luck with this so far, in part because it has been hard to test with the current on_change being in the data_editor. As for the on_change issue, ideally, I’d prefer not to make a separate button that saves the changes before applying the filters as this is going to impact the UX a lot. If anyone knows a good way to apply an on_change to dynamic_filters, I’d really appreciate it.
I’ve been testing in the streamlit playground while I get to grips with it, here is the code so far.
import streamlit as st
import pandas as pd
import numpy as np
from streamlit_dynamic_filters import DynamicFilters
num_rows = 500
np.random.seed(42)
data = []
if 'df1' not in st.session_state :
for i in range(num_rows):
data.append(
{
"Preview": f"https://picsum.photos/400/200?lock={i}",
"Views": np.random.randint(0, 1000),
"Active": np.random.choice([True, False]),
"Category": np.random.choice(["🤖 LLM", "📊 Data", "⚙️ Tool"]),
"Progress": np.random.randint(1, 100),
}
)
st.session_state.df1 = pd.DataFrame(data)
st.session_state.edited_df1 = st.session_state.df1.copy()
# Convenient shorthand notation
df1 = st.session_state.df1
# Save edits by copying edited dataframes to "original" slots in session state
def save_edits():
merged_df = pd.merge(st.session_state.df1,st.session_state.edited_df1,how='outer',indicator=True)
#st.session_state.df1 = st.session_state.edited_df1.copy()
diff_df1 = merged_df[merged_df['_merge'] == 'right_only']
st.write(diff_df1)
#[INSERT CODE THAT FINDS EDITED VALUES AND UPDATES relevant rows of df1]
if st.toggle("Show df", on_change=save_edits):
dynamic_filters = DynamicFilters(df1, filters = ['Category','Active']) #on_change here raises errors
with st.sidebar :
st.write("Dataframe Filters")
dynamic_filters.display_filters() #on_change here raises errors
progress = st.slider("Progress", 1,100, 100,on_change=save_edits)
filtered_df = dynamic_filters.filter_df()
st.session_state.edited_df1 = st.data_editor(filtered_df[filtered_df['Progress']<progress], on_change=save_edits, hide_index=True, use_container_width=True, num_rows="dynamic") #Currently includes the on_change but this needs to be moved elsewhere