You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Dash实现按列配置多选下拉筛选器的代码修改求助

为Dash表格的每一列添加独立多选下拉筛选菜单

问题背景

我有如下数据集:

cat_id author year publisher country value (dollars)
name1 kunga 1998 D and D Australia 10
name2 siba 2001 D and D UK 20
name3 siba 2001 D and D US 20
name3 shevara 2001 D and D UK 10
name3 dougherty 1992 D and D Australia 20
name4 ken 2011 S and K Australia 10

需求是为表格的每一列添加独立的Multi-Select Dropdown(多选下拉菜单),而非表格级别的下拉(当前单列筛选无法满足一次性多选过滤的需求)。例如,cat_id列下方的下拉需包含name1、name2、name3、name4;author列下拉需包含kunga、siba等选项。

我已编写了如下Dash代码,但存在两个问题:

  1. 当前下拉菜单是表格级别的,而非按列配置;
  2. 无法基于下拉选项过滤表格数据。

我知道问题出在以下代码段,且缺少对应的回调逻辑,但作为Dash新手,不清楚具体修改方式,请求帮忙调整代码解决这两个问题:

#Need to move this to being within each column?
html.Label('Multi-Select Dropdown'),
dcc.Dropdown(options=[{'label':i,'value':i} for i in df.columns], value = [i for i in df.columns], multi=True),

原完整代码

import dash
from dash.dependencies import Input, Output, State
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

app = dash.Dash(__name__)
df = pd.read_excel('dash_test_doc.xlsx')

app.layout = html.Div([
    html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10},),
        html.Button('Add Column', id='adding-rows-button', n_clicks=0)
    ], style={'height': 50}),
    #Need to move this to being within each column?
    html.Label('Multi-Select Dropdown'),
    dcc.Dropdown(options=[{'label':i,'value':i} for i in df.columns], value = [i for i in df.columns], multi=True),
    dash_table.DataTable(
        id='adding-rows-table',
        columns=[{"name": i, "id": i,"deletable":True} for i in df.columns],
        # column_conditional_dropdowns=[{'id':i,'dropdowns':df[i]} for i in df.columns],
        data = df.to_dict('rows'),
        editable=True,
        filtering=True,
        sorting=True,
        sorting_type="multi",
        row_selectable="multi",
        row_deletable=True,
        selected_rows=[],
        pagination_mode="fe",
        style_cell_conditional=[
            {
                'if': {'row_index': 'odd'},
                'backgroundColor': 'rgb(230, 255, 230)'
            }
        ] + [
            {
                'if': {'column_id': c},
                'textAlign': 'left'
            } for c in ['Date', 'Region']
        ],
        style_header={
            'backgroundColor': 'white',
            'fontWeight': 'bold'
        }
    ),
    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    dcc.Graph(id='adding-rows-graph')
])

@app.callback(
    Output('adding-rows-table', 'data'),
    [Input('editing-rows-button', 'n_clicks')],
    [State('adding-rows-table', 'data'), State('adding-rows-table', 'columns')])
def add_row(n_clicks, rows, columns):
    if n_clicks > 0:
        rows.append({c['id']: '' for c in columns})
    return rows

@app.callback(
    Output('adding-rows-table', 'columns'),
    [Input('adding-rows-button', 'n_clicks')],
    [State('adding-rows-name', 'value'), State('adding-rows-table', 'columns')])
def update_columns(n_clicks, value, existing_columns):
    if n_clicks > 0:
        existing_columns.append({
            'id': value,
            'name': value,
            'editable_name': True,
            'deletable': True
        })
    return existing_columns

@app.callback(
    Output('adding-rows-graph', 'figure'),
    [Input('adding-rows-table', 'data'), Input('adding-rows-table', 'columns')])
def display_output(rows, columns):
    return {
        'data': [{
            'type': 'heatmap',
            'z': [[row.get(c['id'], None) for c in columns] for row in rows],
            'x': [c['name'] for c in columns]
        }]
    }

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

解决方案

要实现每列独立的多选下拉筛选,我们需要做两个核心修改:为每个列生成独立的下拉菜单,并编写回调逻辑根据选中的选项过滤表格数据。以下是调整后的完整代码:

import dash
from dash.dependencies import Input, Output, State
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd

app = dash.Dash(__name__)
df = pd.read_excel('dash_test_doc.xlsx')
# 保存原始数据,用于过滤时的数据源(避免过滤后的数据被二次修改)
original_data = df.to_dict('rows')

app.layout = html.Div([
    html.Div([
        dcc.Input(
            id='adding-rows-name',
            placeholder='Enter a column name...',
            value='',
            style={'padding': 10},),
        html.Button('Add Column', id='adding-rows-button', n_clicks=0)
    ], style={'height': 50}),
    
    # 为每一列创建独立的多选下拉菜单,横向排列
    html.Div([
        html.Div([
            html.Label(f"筛选 {col}"),
            dcc.Dropdown(
                id=f'dropdown-{col}',
                options=[{'label': str(val), 'value': str(val)} for val in df[col].unique()],
                value=[str(val) for val in df[col].unique()],
                multi=True
            )
        ], style={'width': '18%', 'display': 'inline-block', 'margin-right': '2%', 'margin-bottom': '10px'}) 
        for col in df.columns
    ], style={'margin': '20px 0'}),
    
    dash_table.DataTable(
        id='adding-rows-table',
        columns=[{"name": i, "id": i,"deletable":True} for i in df.columns],
        data = df.to_dict('rows'),
        editable=True,
        sorting=True,
        sorting_type="multi",
        row_selectable="multi",
        row_deletable=True,
        selected_rows=[],
        pagination_mode="fe",
        style_cell_conditional=[
            {
                'if': {'row_index': 'odd'},
                'backgroundColor': 'rgb(230, 255, 230)'
            }
        ] + [
            {
                'if': {'column_id': c},
                'textAlign': 'left'
            } for c in df.columns
        ],
        style_header={
            'backgroundColor': 'white',
            'fontWeight': 'bold'
        }
    ),
    html.Button('Add Row', id='editing-rows-button', n_clicks=0),
    dcc.Graph(id='adding-rows-graph')
])

# 新增:整合过滤+添加行的回调逻辑
@app.callback(
    Output('adding-rows-table', 'data'),
    [Input(f'dropdown-{col}', 'value') for col in df.columns] + [Input('editing-rows-button', 'n_clicks')],
    [State('adding-rows-table', 'columns')])
def filter_and_add_row(*args):
    # 拆分参数:前N个是各列下拉的选中值,接下来是添加行按钮的点击次数,最后是表格列信息
    dropdown_values = args[:len(df.columns)]
    n_clicks = args[len(df.columns)]
    columns = args[len(df.columns)+1]
    
    # 1. 执行过滤逻辑:基于原始数据,保留符合所有列筛选条件的行
    filtered_data = original_data.copy()
    for col, selected_vals in zip(df.columns, dropdown_values):
        filtered_data = [row for row in filtered_data if str(row[col]) in selected_vals]
    
    # 2. 执行添加行逻辑:如果按钮被点击,新增空行
    if n_clicks > 0:
        filtered_data.append({c['id']: '' for c in columns})
    
    return filtered_data

@app.callback(
    Output('adding-rows-table', 'columns'),
    [Input('adding-rows-button', 'n_clicks')],
    [State('adding-rows-name', 'value'), State('adding-rows-table', 'columns')])
def update_columns(n_clicks, value, existing_columns):
    # 新增列时校验输入值非空
    if n_clicks > 0 and value:
        existing_columns.append({
            'id': value,
            'name': value,
            'editable_name': True,
            'deletable': True
        })
    return existing_columns

@app.callback(
    Output('adding-rows-graph', 'figure'),
    [Input('adding-rows-table', 'data'), Input('adding-rows-table', 'columns')])
def display_output(rows, columns):
    # 优化热力图数据处理:将非数值转为None,避免报错
    z_data = []
    for row in rows:
        row_vals = []
        for c in columns:
            val = row.get(c['id'], None)
            try:
                row_vals.append(float(val) if val != '' else None)
            except (ValueError, TypeError):
                row_vals.append(None)
        z_data.append(row_vals)
    
    return {
        'data': [{
            'type': 'heatmap',
            'z': z_data,
            'x': [c['name'] for c in columns]
        }]
    }

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

修改说明:

  1. 为每列创建独立下拉菜单:

    • 用列表推导式为每个列生成专属的下拉组件,每个组件ID设为dropdown-{列名}保证唯一性。
    • 下拉选项直接取自对应列的唯一值,默认选中所有选项,符合初始展示全部数据的需求。
    • 用inline-block让下拉菜单横向排列,节省页面空间。
  2. 新增过滤+添加行的整合回调:

    • 把所有列的下拉选中值和添加行按钮作为输入,确保任何一个筛选条件变化或添加行操作都能触发更新。
    • 基于原始数据做过滤,避免过滤后的二次数据被修改导致的筛选异常。
    • 同时保留原有的添加行逻辑,保证功能不丢失。
  3. 其他细节优化:

    • 移除了原表格的filtering=True,因为我们用自定义下拉筛选替代了内置筛选,避免功能冲突。
    • 调整表头对齐逻辑,适配所有列的展示需求。
    • 优化热力图的数据处理逻辑,处理非数值列和空值的情况,避免图表渲染报错。

内容的提问来源于stack exchange,提问作者Slowat_Kela

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:10:20