New Component: Dynamic multi-select filters

Hi Team ,
I am new to streamlit-aggrid. I have a CSV file which I am loading into a pandas dataframe and creating an Aggrid table on top of it with filters for all the columns present in the dataframe. I have also added other properties to add features like automatic text-wrapping for the cells and paginations, enabled pivot and row grouping etc.

My requirement is I want to dynamically resize the aggrid table size on basis on my filter’s selections as I want to avoid the empty space it is creating at the below in case of small no of rows.
FYI, my filtered data can have 1 as record as well.
Therefore I want to calculate the filter table size on runtime and decide the height of the aggrid table.
Can anyone help me and provide any direction on how can I achieve this.
aggrid_dynamic_vertical_sizing-min

Hi @Oleksandr_Arsentiev! First of all you created a great component! Thank you for creating this.
My problem is I am trying to apply dynamic filters on multiple tabs in the same page and I am getting the error of creating multiselect with same key. I tried using filter_name but that does not help me in this case. filter_name only helps me in the multipage architecture.
Thanks!

pls try the following:

def clear_cache():
    keys = list(st.session_state.keys())
    for key in keys:
        st.session_state.pop(key)

st.button('Reset All Filters', on_click=clear_cache)

Hello! I didn’t work with Streamlit tabs yet but I would think that using filters_name argument when initializing DynamicFilters should work. Could you provide code snippet?

Am worried!
I have a simple dataset with both continuous and categorical data. However, am getting the error below. Please guide!

File "C:\Users\ADMIN\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\streamlit\runtime\scriptrunner\script_runner.py", line 584, in _run_script
    exec(code, module.__dict__)File "C:\Users\ADMIN\Desktop\PROJECTS\Salary Prediction\app.py", line 202, in <module>
    dynamic_filters=DynamicFilters(df, filters=['Gender','Job Title']).display_filters()
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^File "C:\Users\ADMIN\AppData\Local\Packages\PythonSoftwareFoundation.Python.3.11_qbz5n2kfra8p0\LocalCache\local-packages\Python311\site-packages\streamlit_dynamic_filters\dynamic_filters.py", line 173, in display_filters
    selected = st.multiselect(f"Select {filter_name}", sorted(options),
                                                       ^^^^^^^^^^^^^^^

This is my used script:

from streamlit_dynamic_filters import DynamicFilters
with st.sidebar:
df=pd.read_csv(‘Salary Prediction dataset.csv’)
dynamic_filters=DynamicFilters(df, filters=[‘Gender’,‘Job Title’]).display_filters()

Hi, @Oleksandr_Arsentiev it seems like the hierarchy dropdown menu only works from top to bottom. If we select a filter from the bottom, the top dropdown menu won’t change.

I think it won’t work well for continious data since by default this component uses multiselect filters. It’s best to use it for categorical data only

Thanks to the contributors, there are some updates in the component:

Ability to reset all filters easily:

st.button("Reset Filters", on_click=dynamic_filters.reset_filters)

Added key for each filter, so that multiple DynamicFilters can be used in one app avoiding DuplicateWidgetID error.

Please use the latest version:

pip install streamlit-dynamic-filters==0.1.9

@Atharva @Filipa

Hi @Oleksandr_Arsentiev,

Thank you for this great package! It’s exactly what I want to implement in my project.

However, there’s just one little thing I need. I use st.dataframe() with the arguments on_select=‘rerun’ and selection_mode=‘multi_row’ to enable user selections on the dataframe. This feature was introduced in the streamlit==1.35.00 update. I use the return value of the dataframe to handle user selections.

I noticed the display_df() function in your repository:

def display_df(self, **kwargs):
“”“Renders the filtered dataframe in the main area.”“”
# Display filtered DataFrame
st.dataframe(self.filter_df(), **kwargs)

Could you add a return value that provides the user selection?

Thank you very much!

Hi @kadir5838 if you just want the user selection from your dataframe, I think you can use this code:

dynamic_filters = DynamicFilters(df, filters=['region', 'country', 'city', 'district'])
dynamic_filters.display_filters(location='sidebar')

# get filtered dataframe
new_df = dynamic_filters.filter_df()
user_selection = st.dataframe(new_df, on_select="rerun", selection_mode="multi-row")

# check user selected rows
st.write(user_selection.selection.rows)

let me know if this works!

Amazing. It works very well. Im using dynamic selectbox by using session state. They filter each other well but i have to click twice in each selecbox option. Did you explore with selectbox objetc?
Thank you!

Is there a way to use values in my data as the filter options rather than entering them as a list?

Hi, thank you for the contribution!

This is my code snippet so far:

Retrieving all the valid filter combinations of attributes from Ferda

column_names = [“Operator Group”, “Operator”, “Region”, “Country”, “Quarter”]
all_filter_valid_combinations_df = st.session_state.all_combinations_df

Creating an instance of the Dynamic filter class - Documentation: “https://dynamic-filters-demo.streamlit.app/Columns_Example

dynamic_filters = DynamicFilters(all_filter_valid_combinations_df, filters=column_names)

Displaying the dynamic filter

dynamic_filters.display_filters(location=‘columns’, num_columns=1)

Getting the filtered DataFrame based on user selections

filtered_df = dynamic_filters.filter_df()

Extracting the selected filters from the filtered DataFrame

operator_group = filtered_df[‘Operator Group’].unique().tolist()
operator = filtered_df[‘Operator’].unique().tolist()
region = filtered_df[‘Region’].unique().tolist()
country = filtered_df[‘Country’].unique().tolist()
quarter = filtered_df[‘Quarter’].unique().tolist()

Creating a user_filter dictionary from the user selected filters:

user_filter = create_user_filter_dict(operator_group, operator, region, country, quarter)

I just have 2 questions please, I need to fix as fast as possible:

  1. I need to make the “Operator” filter mandatory - in other words, the user must select an Operator
  2. I need to make the Operator Group, and Operator as st.selectbox (in order words it needs to be a single select as opposed to a multiselect)

can you please please help me? Thank you!

this looks awesome and just what i have been looking for

Thank you for this awesome component.

I am just using this component inside st.form in order to give user flexibility to select all the filter value once and then submit.

The expectation here is that when I click on form’s submit button it should take all the filter values into account and filter the dataframe accordingly. However, it does not work as expected

Here is my code

import streamlit as st
import pandas as pd
from streamlit_dynamic_filters import DynamicFilters

# Sample data
data = {
    'Region': ['North America', 'North America', 'Europe', 'Oceania',
               'North America', 'North America', 'Europe', 'Oceania',
               'North America', 'North America', 'Europe', 'Oceania'],
    'Country': ['USA', 'Canada', 'UK', 'Australia',
                'USA', 'Canada', 'UK', 'Australia',
                'USA', 'Canada', 'UK', 'Australia'],
    'City': ['New York', 'Toronto', 'London', 'Sydney',
             'New York', 'Toronto', 'London', 'Sydney',
             'New York', 'Toronto', 'London', 'Sydney'],
    'District': ['Manhattan', 'Downtown', 'Westminster', 'CBD',
                 'Brooklyn', 'Midtown', 'Kensington', 'Circular Quay',
                 'Queens', 'Uptown', 'Camden', 'Bondi']
}
df = pd.DataFrame(data)

with st.form("Apply Filters"):
    dynamic_filters = DynamicFilters(df=df, filters=['Region', 'Country', 'City', 'District'], filters_name='test_filter')
    dynamic_filters.display_filters(location='columns', num_columns=2, gap='small')
    submitted = st.form_submit_button("Submit")

dynamic_filters.display_df(use_container_width=True, hide_index=True)

Below is the demo showing the unexpected behavior

screen-capture