Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dash DataTable drop-down filter

Dash DataTable component offers a free-text filter under the column header.

Dash free-text filter

How can I replace this filter with a drop-down filter that contains all unique values to select from?

Something that is available off-the-shelf in Excel:

Excel drop-down filter

or with some simple hack in DataTables JavaScript component

DataTables drop-down filter

I know I could use drop-drop down filter outside of a DataTable that interacts with DataTable via callbacks but I want the drop-down filter to be part of DataTable column header. In this sense, I don't want to use solution from: display datatable after filtering rows from dropdown list in dash

like image 884
dzieciou Avatar asked Jun 25 '26 20:06

dzieciou


1 Answers

Option #1: Using Dash's built-in DataTable dropdown attributes

Answer Updated: 2023

E.g., with a simple project setup like the following:

.
├── app.py
└── assets
    └── custom.css

2 directories, 2 files

where assets/custom.css contains:

.dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner td, .dash-table-container .dash-spreadsheet-container .dash-spreadsheet-inner th {
    text-align: center !important;
    width: 20%
}

and app.py contains:

import pandas as pd
import dash
from dash import Dash, Input, Output, dcc, html, dash_table, State

# Sample filter & data
place_holder = None
df_filter = pd.DataFrame(
    {
        "Person": [place_holder],
        "Age": [place_holder],
        "Gender": [place_holder],
        "Occupation": [place_holder],
    }
)
df_data = pd.DataFrame(
    {
        "Person": ["Alice", "Brian", "Chris", "David", "Emily"],
        "Age": [25, 30, 36, 25, 36],
        "Gender": ["Female", "Male", "Male", "Male", "Female"],
        "Occupation": ["Engineer", "Doctor", "Lawyer", "Artist", "Artist"],
    }
)


app = Dash(__name__)

app.layout = html.Div(
    [
        html.H1("DataTable with Header Dropdowns for Filtering"),
        html.H2("Filtering Table"),
        html.Div(
            [
                dash_table.DataTable(
                    id="table-filter",
                    columns=[
                        {"name": i, "id": i, "presentation": "dropdown"}
                        for i in df_filter.columns
                    ],
                    data=df_filter.to_dict("records"),
                    editable=True,
                    dropdown={
                        col: {
                            "options": [
                                {"label": str(i), "value": str(i)}
                                for i in df_data[col].unique()
                            ],
                        }
                        for col in df_data.columns
                    },
                )
            ]
        ),
        dash_table.DataTable(
            id="table-data",
            columns=[{"name": i, "id": i} for i in df_data.columns],
            data=df_data.to_dict("records"),
            style_table={"overflowX": "scroll"},
            css=[{"selector": "tr:first-child", "rule": "display: none",},],
        ),
        html.Div(id="table-output"),
    ],
    # NOTE The CSS margin set below and also the width set in the
    # custom.css file depends on the number of columns in this example.
    # Alternative &/or additional CSS (e.g., overflow) would be probably
    # best for data tables with many more columns.
    style={"textAlign": "center", "margin": "5%"},
)


# Callback for updating table based on dropdown selection
@app.callback(
    Output("table-data", "data"),
    [Input("table-filter", "data_timestamp")],
    [State("table-filter", "data"), State("table-data", "data")],
)
def update_table(timestamp, filter_rows, current_data):
    if timestamp is None:
        raise dash.exceptions.PreventUpdate

    data = df_data.copy()
    cols = data.columns

    for col, value in filter_rows[0].items():
        if value is not None:
            data = data[data.astype(str)[col] == value]

    return data.to_dict("records")


app.run_server(debug=True)

results in: Video screen shot recording showing dash app with Excel-like dropdown column filtering Note, however, that it is currently not possible to set multi=True for dropdowns within Dash DataTables (like it is for dash.dcc.Dropdown components). If such filtering ability is required, it would probably be best to use the built-in filtering capability of Dash DataTables, or, see Option #2 below.

Option #2: Using dropdowns outside of the Dash DataTable

import dash
import pandas as pd

from dash import dash_table as dt
from dash import dcc
from dash import html
from dash.dependencies import Input
from dash.dependencies import Output

df = pd.read_csv("https://raw.githubusercontent.com/plotly/datasets/master/solar.csv")

app = dash.Dash(__name__)

states = df.State.unique().tolist()

app.layout = html.Div(
    children=[
        dcc.Dropdown(
            id="filter_dropdown",
            options=[{"label": st, "value": st} for st in states],
            placeholder="-Select a State-",
            multi=True,
            value=df.State.values,
        ),
        dt.DataTable(
            id="table-container",
            columns=[{"name": i, "id": i} for i in df.columns],
            data=df.to_dict("records")
        )
    ]
)


@app.callback(
    Output("table-container", "data"), 
    Input("filter_dropdown", "value")
)
def display_table(state):
    dff = df[df.State.isin(state)]
    return dff.to_dict("records")


if __name__ == "__main__":
    app.run_server(debug=True)

app first display

→ Select red ❌ to Clear all

cleared table

selected options from dropdown

If you don't want the selected options to always be present/shown:

One option would be to add the following CSS to a local ./assets/custom.css file:

.Select--multi .Select-value {
    display: none;
}

which would result in the following behavior:

options not shown

(you could add some title above the dropdown, or something, to indicate to "select a state", etc.)

options are still in fact chosen

Note: In this implementation case, you may likely want to additionally add the parameter row_deletable=True to the dt.DataTable. However, I just tested this, and it does not automatically repopulate the dropdown with options, so, further code would need to be invented to address that, which I imagine is possible. But you get the idea.

like image 120
John Collins Avatar answered Jun 28 '26 09:06

John Collins