Summary
Hi, I am creating this topic to Learn/Teach good practices of Streamlit code.
First things first:
- I Love Streamlit (Team and Community)
- Being a beginner at coding, I accomplished more than I ever thought possible because of the Front-end and “tangibility” to the problem-solving that Streamlit provides.
Please feel free to add whatever you may see fit.
Good channels to check:
Misra Turp for Pandas / Streamlit
- Her Session State videos saved me a lot of time.
Avra for Streamlit/AI
- Has many videos building real Streamlit apps, which are very, very helpful!
Fanilo for Streamlit
- His overview of Streamlit Components is awesome!
My 2 cents on: Session State;
(For Noobs like me )
Session State is used when you want your app to “Remember” things.
Every time you click a Streamlit widget, your app runs linearly from top to bottom, and it forgets things that you did, variables, user inputs, etc.
To make your app “remember” what you did, we create a session state variable, which is “memorized” by the app and can be re-used.
STEP 1: Think about the problem; What information do you need the app to remember?
In my case I have a dataframe, and I want to allow the user to create as many filters as he/she would like; So I need two buttons “Add Filter” and “Remove Filter”;
When the button is clicked, a st.Selectbox will appear, and the user will select the filter option;
So this is the problem:
Since my app reruns, when I click button ‘Add Filter’ it will add the filter1; but it won’t remember how many filters I currently have.
So I need the app to remember the number of filters that I want to display, and button will increase the number of filters by 1;
STEP 2: Initialize Session State variable;
Import streamlit as st
if "number_of_filters" not in st.session_state:
st.session_state.number_of_filters = 0
This is stored as a dictionary;
st.session_state['name of what you want to store'] = 'value of what you want to store'
In my case: st.session_state['number_of_filters'] = 0
Tip: do you want to know what your app remembers? I always display this at the beginning, so I can see what it remembers at first, and I write it at the end as well, to see what has changed;
st.write(st.session_state)
at the beginning
st.write(st.session_state)
at the end of the app
STEP 3: Create the buttons; it will increase the value of session state when clicked;
if st.button("Add",key='add_filter_btn',use_container_width=True):
st.session_state.number_of_filters += 1
if st.button("Remove",key='remove_filter_btn',use_container_width=False) and st.session_state.number_of_filters > 0:
st.session_state.number_of_filters -= 1
STEP 4: Create a number of filters based on the value of st.session_state['number_of_filters'];
Problem: each widget has a name, and the name needs to be unique. The “name” of the widget is the key, because it is also stored in st.session_state, in my case.
In my case I want the user to select a column name from the list of available columns, and then filter the dataframe on possible column values for the selected column.
First I create a copy of the df I want to use, so I can apply each filter on the “smaller dataframe”
filtered_df = df.copy()
Now I use the “number_of_filters” in session_state to create n filters.
for number_filter in range(st.session_state.number_of_filters):
# this will give me available columns on my filtered dataframe
option = filtered_df.columns.tolist()
#Remember we need the unique_key for the widget
unique_key_selectbox = f"{number_filter}_selectbox" #this is my unique_key needed on the selectbox
selectbox_result = st.selectbox(options ,key= unique_key_selectbox)
# now based on the selected column, I want to allow the user to filter by using a multiselect, so first I create a list of options on the multiselect;
multiselect_options = filtered_df[selectbox_result].unique().tolist()
unique_key_multiselect = f"{number_filter}_multiselect"
multiselect_box = st.multiselect(multiselect_options, key = unique_key_multiselect)
# Finally I filter the dataframe on the selected value, which will be used on the next filter on the "for" loop
filtered_df = filtered_df.loc[df[selet_box].isin(multiselect_box)]
Now my question:
- Can you share good practices on how you organize your code of a complex streamlit app?
- Do you have a utils folder with your functions?
- Do you store css separately?
- Can you please give-me examples?