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

Plotly Dash下拉框实现全选与多级级联联动方法

解决方案

核心问题定位

你现有代码存在三个逻辑缺陷导致需求无法实现:

  • 州下拉框的选项加载逻辑仅绑定了顶层Unit单选值,没有关联已选中的国家值,无法实现选中国家后过滤下属州的联动效果
  • 所有筛选逻辑都是强制单值精确匹配,没有预留全选的特殊判断分支,也没有在下拉选项中加入全选入口
  • 下级下拉框的值没有随上级选项更新重置,容易出现选值不在新选项列表中的空数据bug

具体修改方案

级联联动实现逻辑

  • 调整下拉框回调的输入依赖:州下拉框的选项加载同时监听radio_items(Unit选择)和select_country(国家选择)两个输入,只返回当前选中Unit+选中国家下的所有州
  • 增加上级选值变化时重置下级值的逻辑:当Unit变化时,重置患者状态、国家、州的选中值;当国家变化时,重置州的选中值,避免无效选值
  • 图表数据过滤时,按层级依次匹配筛选条件,保证选中国家后只加载对应国家数据,选中州后只加载对应州下的城市统计数据

全选功能实现逻辑

  • 定义全局特殊常量ALL_OPTION_VALUE = '__ALL__'作为全选的标识值,避免和真实业务数据重名
  • 每个下拉框生成选项时,在列表最前面插入{'label': '全选', 'value': ALL_OPTION_VALUE}选项
  • 数据过滤时增加判断:如果对应筛选字段的选中值是ALL_OPTION_VALUE,就跳过该字段的过滤条件,返回该字段下所有数据
  • 所有下拉框的默认初始值设为ALL_OPTION_VALUE,初始加载时默认展示全量数据

修改后完整代码

from dash import dcc, html, Dash
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd

# 定义全选的特殊标识值,避免和真实数据冲突
ALL_OPTION_VALUE = '__ALL__'

PatientStatus = pd.read_csv('PatientStatus.csv')
app = Dash(__name__, meta_tags=[{"name": "viewport", "content": "width=device-width"}])

app.layout = html.Div([
    html.Div([
        html.Div([
            html.Div([
                html.H3('Patient Count', style={'margin-bottom': '0px', 'color': 'black'}),
            ])
        ], className="create_container1 four columns", id="title"),
    ], id="header", className="row flex-display", style={"margin-bottom": "25px"}),
    
    html.Div([
        html.Div([
            html.P('Select Unit Name', className='fix_label', style={'color': 'black'}),
            dcc.RadioItems(id='radio_items',
                           labelStyle={"display": "inline-block"},
                           value='Testing',
                           options=[{'label': i, 'value': i} for i in PatientStatus['UNITNAME'].unique()],
                           style={'text-align': 'center'}, className='dcc_compon'),
            html.P('Select Patient Status:', className='fix_label', style={'color': 'black', 'margin-top': '30px'}),
            dcc.Dropdown(id='select_county',
                         multi=False,
                         clearable=True,
                         disabled=False,
                         style={'display': True},
                         placeholder='Patient Status',
                         value=ALL_OPTION_VALUE,
                         options=[], className='dcc_compon'),
            html.P('Select Country:', className='fix_label', style={'color': 'black', 'margin-top': '30px'}),
            dcc.Dropdown(id='select_country',
                         multi=False,
                         clearable=True,
                         disabled=False,
                         style={'display': True},
                         placeholder='Select Country',
                         value=ALL_OPTION_VALUE,
                         options=[], className='dcc_compon'),
            html.P('Select State:', className='fix_label', style={'color': 'black', 'margin-top': '30px'}),
            dcc.Dropdown(id='select_state',
                         multi=False,
                         clearable=True,
                         disabled=False,
                         style={'display': True},
                         placeholder='Select State',
                         value=ALL_OPTION_VALUE,
                         options=[], className='dcc_compon')
        ], className="create_container2 five columns"),
        
        html.Div([
            dcc.Graph(id="bar_chart",
                     config={"displayModeBar": 'hover'}),
        ], className="create_container2 seven columns"),
    ], className="row flex-display"),
], id="maincontainer", style={"display": "flex", "flex-direction": "columns"})


# 加载患者状态选项:仅依赖Unit选择
@app.callback(
    Output('select_county', 'options'),
    Input('radio_items', 'value'))
def get_county_options(radio_items):
    PatientStatus1 = PatientStatus[PatientStatus['UNITNAME'] == radio_items]
    status_list = [{'label': k, 'value': k} for k in PatientStatus1['PATIENTSTATUS'].unique()]
    # 插入全选选项
    return [{'label': '全选', 'value': ALL_OPTION_VALUE}] + status_list


# Unit变化时重置患者状态选中值为全选
@app.callback(
    Output('select_county', 'value'),
    Input('radio_items', 'value'))
def reset_county_value(radio_items):
    return ALL_OPTION_VALUE


# 加载国家选项:仅依赖Unit选择
@app.callback(
    Output('select_country', 'options'),
    Input('radio_items', 'value'))
def get_country_options(radio_items):
    PatientStatus1 = PatientStatus[PatientStatus['UNITNAME'] == radio_items]
    country_list = [{'label': k, 'value': k} for k in PatientStatus1['COUNTRY'].unique()]
    return [{'label': '全选', 'value': ALL_OPTION_VALUE}] + country_list


# Unit变化时重置国家选中值为全选
@app.callback(
    Output('select_country', 'value'),
    Input('radio_items', 'value'))
def reset_country_value(radio_items):
    return ALL_OPTION_VALUE


# 加载州选项:同时依赖Unit选择、国家选择,实现级联过滤
@app.callback(
    Output('select_state', 'options'),
    Input('radio_items', 'value'),
    Input('select_country', 'value'))
def get_state_options(radio_items, select_country):
    PatientStatus1 = PatientStatus[PatientStatus['UNITNAME'] == radio_items]
    # 如果国家不是全选,就只过滤对应国家下的州
    if select_country != ALL_OPTION_VALUE:
        PatientStatus1 = PatientStatus1[PatientStatus1['COUNTRY'] == select_country]
    state_list = [{'label': k, 'value': k} for k in PatientStatus1['STATENAME'].unique()]
    return [{'label': '全选', 'value': ALL_OPTION_VALUE}] + state_list


# Unit或国家变化时,重置州选中值为全选
@app.callback(
    Output('select_state', 'value'),
    Input('radio_items', 'value'),
    Input('select_country', 'value'))
def reset_state_value(radio_items, select_country):
    return ALL_OPTION_VALUE


# 更新图表
@app.callback(Output('bar_chart', 'figure'),
              Input('radio_items', 'value'),
              Input('select_county', 'value'),
              Input('select_country', 'value'),
              Input('select_state', 'value'))
def update_graph(radio_items, select_county, select_country, select_state):
    # 先按Unit过滤基础数据
    PatientStatus2 = PatientStatus[PatientStatus['UNITNAME'] == radio_items].copy()
    # 逐层判断筛选条件,非全选才加过滤规则
    if select_county != ALL_OPTION_VALUE:
        PatientStatus2 = PatientStatus2[PatientStatus2['PATIENTSTATUS'] == select_county]
    if select_country != ALL_OPTION_VALUE:
        PatientStatus2 = PatientStatus2[PatientStatus2['COUNTRY'] == select_country]
    if select_state != ALL_OPTION_VALUE:
        PatientStatus2 = PatientStatus2[PatientStatus2['STATENAME'] == select_state]
    # 聚合城市维度的患者数
    PatientStatus3 = PatientStatus2.groupby(['UNITNAME','PATIENTSTATUS','COUNTRY','STATENAME', 'CITYNAME'])['PATIENTCOUNTS'].sum().reset_index()

    # 组装标题展示文本
    title_parts = [f'UNIT ({radio_items})']
    title_parts.append(f'Patient Status({select_county if select_county != ALL_OPTION_VALUE else "全部"})')
    title_parts.append(f'Country({select_country if select_country != ALL_OPTION_VALUE else "全部"})')
    title_parts.append(f'State({select_state if select_state != ALL_OPTION_VALUE else "全部"})')

    return {
        'data':[go.Bar(
                    x=PatientStatus3['CITYNAME'],
                    y=PatientStatus3['PATIENTCOUNTS'],
                    text = PatientStatus3['PATIENTCOUNTS'],
                    texttemplate = 'Patient Count: ' + '%{text:.2s}',
                    textposition = 'auto',
                    marker = dict(
                    color = PatientStatus3['PATIENTCOUNTS'],
                    colorscale = 'phase',
                    showscale = False),
                    hoverinfo='text',
                    hovertext=
                    '<b>Unit</b>: ' + PatientStatus3['UNITNAME'].astype(str) + '<br>' +
                    '<b>Country</b>: ' + PatientStatus3['COUNTRY'].astype(str) + '<br>' +
                    '<b>State</b>: ' + PatientStatus3['STATENAME'] + '<br>' +
                    '<b>City</b>: ' + PatientStatus3['CITYNAME'].astype(str) + '<br>' +
                    '<b>Patient Count</b>: ' + [f'{x:,.0f}' for x in PatientStatus3['PATIENTCOUNTS']] + '<br>'
        )],
        'layout': go.Layout(
            barmode = 'group',
            plot_bgcolor='#F2F2F2',
            paper_bgcolor='#F2F2F2',
            title={
                'text': ' : '.join(title_parts),
                'y': 0.96,
                'x': 0.5,
                'xanchor': 'center',
                'yanchor': 'top'},
            titlefont={
                'color': 'rgb(50, 50, 50)',
                'size': 15},
            hovermode='x',
            xaxis=dict(title='<b>City</b>',
                        color='rgb(50, 50, 50)',
                        showline=True,
                        showgrid=True,
                        linecolor='rgb(50, 50, 50)',
                        linewidth=1),
            yaxis=dict(title='<b>Patient-counts</b>',
                        color='rgb(50, 50, 50)',
                        showline=False,
                        showgrid=True,
                        linecolor='rgb(50, 50, 50)'),
            legend = {
                'orientation': 'h',
                'bgcolor': '#F2F2F2',
                'x': 0.5,
                'y': 1.25,
                'xanchor': 'center',
                'yanchor': 'top'},
            font = dict(
                family = "Arial",
                size = 12,
                color = 'rgb(50, 50, 50)'),
            uniformtext_mode = 'hide',
            uniformtext_minsize = 15
        )       
    }
       
if __name__=='__main__':
    app.run_server(host="localhost", debug=True, use_reloader=False, port=90)

注意事项

  • 确保你的csv文件中UNITNAME/PATIENTSTATUS/COUNTRY/STATENAME/CITYNAME/PATIENTCOUNTS字段名和代码中完全一致,否则会报字段不存在错误
  • 如果需要支持多选全选,只需要把对应Dropdown的multi参数改为True,全选逻辑判断改为判断选中值是否包含ALL_OPTION_VALUE即可
  • 代码里修正了你原布局中create_cotainer2的类名拼写错误,避免样式加载异常

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 08:36:28