Filter_dataframe using submit button click

Summary

Trying to wrap around postback clicks using submit button.
Can any one help or give suggestions please.
Tried various combinations but no luck.
Below is the code.
Thanks…

Steps to reproduce

Code snippet:

#python -m streamlit run filter_dataframe.py
import pandas as pd
import streamlit as st
import datetime
from pandas.api.types import (
    is_categorical_dtype,
    is_datetime64_any_dtype,
    is_numeric_dtype,
    is_object_dtype,
)

st.title("Auto Filter Dataframes in Streamlit")

st.write(
    """This app accomodates the blog [here](https://blog.streamlit.io/auto-generate-a-dataframe-filtering-ui-in-streamlit-with-filter_dataframe/)
    and walks you through one example of how the Streamlit
    Data Science Team builds add-on functions to Streamlit.
    """
)


def filter_dataframe(df: pd.DataFrame) -> pd.DataFrame:
    """
    Adds a UI on top of a dataframe to let viewers filter columns
    Args:
        df (pd.DataFrame): Original dataframe
    Returns:
        pd.DataFrame: Filtered dataframe
    """
    #modify = st.checkbox("Add filters")
    modify = f1.checkbox("Add filters")

    if not modify:
        return df

    df = df.copy()

    # Try to convert datetimes into a standard format (datetime, no timezone)
    for col in df.columns:
        if is_object_dtype(df[col]):
            try:
                df[col] = pd.to_datetime(df[col])
            except Exception:
                pass

        if is_datetime64_any_dtype(df[col]):
            df[col] = df[col].dt.tz_localize(None)

    #modification_container = st.container()
    modification_container = f1.container()
    with modification_container:
        
        #to_filter_columns = st.multiselect("Filter dataframe on", df.columns)
        to_filter_columns = f1.multiselect("Filter dataframe on", df.columns)
        colcount = 1
        #f1 = st.form('f'+ str(datetime.datetime.now()))
        with f1:
            for column in to_filter_columns:
                # f1 = f1.form('f1')
                # with f1:
                left, right = st.columns((1, 20))
                left.write("↳")
                # Treat columns with < 10 unique values as categorical
                if is_categorical_dtype(df[column]) or df[column].nunique() < 10:
                    # f1 = st.form('f'+ str(datetime.datetime.now()))
                    # with f1:
                    # st.header(column)
                    # st.header("if is_categorical_dtype(df[column]) or df[column].nunique() < 10:")
                    user_cat_input = right.multiselect(
                        f"Values for {column}",
                        df[column].unique(),
                        default=list(df[column].unique()),
                    )
                    df = df[df[column].isin(user_cat_input)]
                        # f1.text_input(label='Enter some text')
            
                elif is_numeric_dtype(df[column]):
                    _min = float(df[column].min())
                    _max = float(df[column].max())
                    step = (_max - _min) / 100
                    user_num_input = right.slider(
                        f"Values for {column}",
                        _min,
                        _max,
                        (_min, _max),
                        step=step,
                    )
                    df = df[df[column].between(*user_num_input)]
                
                elif is_datetime64_any_dtype(df[column]):
                    user_date_input = right.date_input(
                        f"Values for {column}",
                        value=(
                            df[column].min(),
                            df[column].max(),
                        ),
                    )
                    if len(user_date_input) == 2:
                        user_date_input = tuple(map(pd.to_datetime, user_date_input))
                        start_date, end_date = user_date_input
                        df = df.loc[df[column].between(start_date, end_date)]
                else:
                    user_text_input = right.text_input(
                        f"Substring or regex in {column}",
                    )
                    if user_text_input:
                        df = df[df[column].str.contains(user_text_input)]
        # submit_button = f1.form_submit_button(label='Submit')
        # if submit_button:
        #     f1.write(df2.shape)
        #     f1.dataframe(df2)

    f1.write(df.shape)
    f1.dataframe(df.head())
    submit_button = f1.form_submit_button(label='Submit')
    if submit_button:
        st.write('sub clicked')
        st.write(df2.shape)
    else:
        st.write('no click')
    return df


df = pd.read_csv(
    "https://raw.githubusercontent.com/mcnakhaee/palmerpenguins/master/palmerpenguins/data/penguins.csv"
)
st.header(str(datetime.datetime.now()))
f1 = st.form('f'+ str(datetime.datetime.now()))
df2 = filter_dataframe(df)
submit_button = f1.form_submit_button(label='Submit')
if submit_button:
    st.write('sub clicked')
    st.write(df2.shape)
else:
    st.write('no click')

# f1.write(df2.shape)
# f1.dataframe(df2.head())
# # submit_button = f1.form_submit_button(label='Submit')

# # df2 = filter_dataframe(df)
# # st.write(df2.shape)
# # st.dataframe(df2)

# if submit_button:
#     st.write('sub clicked')
#     st.write(df2.shape)
# else:
#     st.write('no click')
#f1.dataframe(df2.head())

#f1 = st.form('f'+ str(datetime.datetime.now()))
# with f1:
#     df2 = filter_dataframe(df)
#     submit_button = f1.form_submit_button(label='Submit')
#     # st.write(df2.shape)
#     # st.dataframe(df2)
#     if submit_button:
#         st.write(df2.shape)
#         st.dataframe(df2)

One problem is that you are creating a different form in each rerun, because you assign a different key each time. Don’t do that.

Another problem is that you are instantiating the submit button in two places, that is not a good idea either.

1 Like