在Python Dash中能否实现类似Power BI Hierarchy Slicer的多字段过滤?
在Dash中实现类似PowerBI的层级切片器(Hierarchy Slicer)
针对你用4个联动下拉框筛选4级组织架构表格的场景,我们可以通过Dash组件组合+回调逻辑,实现和PowerBI Hierarchy Slicer类似的层级折叠/展开、联动筛选效果,具体实现方案如下:
核心思路
用dash-bootstrap-components.Accordion实现层级折叠/展开的容器,每个层级嵌套dcc.Checklist作为选择项,通过回调实现:
- 父级选择后自动过滤子级可选范围
- 子级选择时自动关联父级筛选逻辑
- 多层级混合选择后实时过滤表格数据
完整实现代码
1. 依赖导入与数据准备
import dash from dash import Dash, dcc, html, Input, Output, callback import dash_bootstrap_components as dbc import pandas as pd # 模拟4级组织架构业务数据 data = [ {"一级部门": "技术部", "二级部门": "后端组", "三级部门": "Java组", "四级部门": "电商业务线", "员工数": 20}, {"一级部门": "技术部", "二级部门": "后端组", "三级部门": "Python组", "四级部门": "数据分析线", "员工数": 15}, {"一级部门": "技术部", "二级部门": "前端组", "三级部门": "Web组", "四级部门": "官网项目", "员工数": 10}, {"一级部门": "市场部", "二级部门": "品牌组", "三级部门": "新媒体组", "四级部门": "抖音运营", "员工数": 8}, {"一级部门": "市场部", "二级部门": "营销组", "三级部门": "线下推广", "四级部门": "华东区域", "员工数": 12}, {"一级部门": "人事部", "二级部门": "招聘组", "三级部门": "校招组", "四级部门": "2024届", "员工数": 5}, ] df = pd.DataFrame(data)
2. 构建层级切片器布局
app = Dash(__name__, external_stylesheets=[dbc.themes.BOOTSTRAP]) # 封装层级切片器组件 def build_hierarchy_slicer(): level1_options = [{"label": dept, "value": dept} for dept in df["一级部门"].unique()] return dbc.Accordion( [ dbc.AccordionItem( [ dcc.Checklist( id="level1-checklist", options=level1_options, value=[], labelStyle={"display": "block", "margin-left": "10px"}, ), html.Div(id="level2-container"), ], title="一级部门", item_id="level1", ) ], start_collapsed=False, className="border rounded p-2", ) # 页面整体布局 app.layout = dbc.Container( [ html.H3("组织架构层级筛选"), dbc.Row( [ dbc.Col(build_hierarchy_slicer(), width=4), dbc.Col(dcc.Graph(id="filtered-table"), width=8), ] ), ], fluid=True, className="mt-4", )
3. 实现层级联动回调
# 更新二级部门选项 @callback( Output("level2-container", "children"), Input("level1-checklist", "value"), ) def update_level2(selected_level1): filter_df = df[df["一级部门"].isin(selected_level1)] if selected_level1 else df level2_options = [{"label": dept, "value": dept} for dept in filter_df["二级部门"].unique()] return dbc.Accordion( [ dbc.AccordionItem( [ dcc.Checklist( id="level2-checklist", options=level2_options, value=[], labelStyle={"display": "block", "margin-left": "20px"}, ), html.Div(id="level3-container"), ], title="二级部门", item_id="level2", ) ], start_collapsed=True, ) # 更新三级部门选项 @callback( Output("level3-container", "children"), Input("level1-checklist", "value"), Input("level2-checklist", "value"), ) def update_level3(selected_level1, selected_level2): filter_df = df.copy() if selected_level1: filter_df = filter_df[filter_df["一级部门"].isin(selected_level1)] if selected_level2: filter_df = filter_df[filter_df["二级部门"].isin(selected_level2)] if filter_df.empty: return html.Div() level3_options = [{"label": dept, "value": dept} for dept in filter_df["三级部门"].unique()] return dbc.Accordion( [ dbc.AccordionItem( [ dcc.Checklist( id="level3-checklist", options=level3_options, value=[], labelStyle={"display": "block", "margin-left": "30px"}, ), html.Div(id="level4-container"), ], title="三级部门", item_id="level3", ) ], start_collapsed=True, ) # 更新四级部门选项 @callback( Output("level4-container", "children"), Input("level1-checklist", "value"), Input("level2-checklist", "value"), Input("level3-checklist", "value"), ) def update_level4(selected_level1, selected_level2, selected_level3): filter_df = df.copy() if selected_level1: filter_df = filter_df[filter_df["一级部门"].isin(selected_level1)] if selected_level2: filter_df = filter_df[filter_df["二级部门"].isin(selected_level2)] if selected_level3: filter_df = filter_df[filter_df["三级部门"].isin(selected_level3)] if filter_df.empty: return html.Div() level4_options = [{"label": dept, "value": dept} for dept in filter_df["四级部门"].unique()] return dcc.Checklist( id="level4-checklist", options=level4_options, value=[], labelStyle={"display": "block", "margin-left": "40px"}, )
4. 表格筛选回调
@callback( Output("filtered-table", "figure"), Input("level1-checklist", "value"), Input("level2-checklist", "value"), Input("level3-checklist", "value"), Input("level4-checklist", "value"), ) def filter_table(selected_level1, selected_level2, selected_level3, selected_level4): filtered_df = df.copy() # 应用各层级筛选条件 if selected_level1: filtered_df = filtered_df[filtered_df["一级部门"].isin(selected_level1)] if selected_level2: filtered_df = filtered_df[filtered_df["二级部门"].isin(selected_level2)] if selected_level3: filtered_df = filtered_df[filtered_df["三级部门"].isin(selected_level3)] if selected_level4: filtered_df = filtered_df[filtered_df["四级部门"].isin(selected_level4)] # 生成可视化表格 return { "data": [ { "type": "table", "header": { "values": list(filtered_df.columns), "fill": {"color": "#1f77b4"}, "font": {"color": "white", "size": 12} }, "cells": { "values": [filtered_df[col] for col in filtered_df.columns], "fill": {"color": ["#f0f0f0", "#ffffff"]}, "font": {"size": 11} } } ], "layout": {"margin": {"l": 10, "r": 10, "t": 20, "b": 20}} } if __name__ == "__main__": app.run_server(debug=True)
优化建议
- 添加全选/取消全选按钮:在每个层级的Checklist旁添加按钮,通过回调实现一键选择当前层级所有选项
- 自定义样式:通过
style或外部CSS调整折叠图标、选项间距、边框样式,让组件更贴近PowerBI的视觉风格 - 组件封装:将层级生成逻辑封装为通用函数,支持动态适配不同层级数量的组织架构
- 空值处理:优化空选择场景的提示,避免表格空白
内容的提问来源于stack exchange,提问作者dshefman
相关产品推荐
相关产品推荐

