Dash Plotly旭日图回调问题:点击类别更新下拉菜单选项
问题描述
我正在编写Dash Plotly代码,计划用旭日图(sunburst chart)展示数据的所有类别,实现用户点击图表中的目标类别后,下拉菜单仅显示该类别对应的基金名称。但尝试多种回调实现方式均失败,现将使用的DataFrame及现有代码附上,请求协助修正回调逻辑。
所用DataFrame代码
import pandas as pd df = {'Nome': ['Aberdeen Multi Asset Growth Advisory FIM IE', 'Absolute Alpha Global Advisory FIC FIM', 'Absolute Alpha Marb Advisory FIC FIM', 'Absolute Hedge FIC FIM', 'Claritas Hedge Advisory FIC FIM Longo Prazo', 'MOS Institucional FIA', 'Occam Long Biased Advisory FIC FIM', 'Sharp Equity Value Advisory FIC FIA', 'Vinland Macro Advisory FIC FIM'], 'tipo':['Internacional Multimercado Hedgeado', 'Multiestratégia', 'Internacional Multimercado Hedgeado', 'Macro Média Vol', 'Macro Média Vol', 'Renda Variável Long Only', 'Renda Variável Long Biased', 'Renda Variável Long Only', 'Macro Média Vol'], 'retorno 1 mês':[ '0,79%', '0,38%', '0,40%', '0,38%', '0,82%', '4,11%', '4,02%', '4,55%', '0,72%'], 'classes' : [ 'Internacional Multimercado Hedgeado', 'Multiestratégia', 'Macro Média Vol', 'Renda Variável Long Only', 'Renda Variável Long Biased', 'Macro Alta Vol', 'Quantitativo', 'Long Short Direcional', 'Long Short Neutro'], 'número de fundos': ['8', '6', '10', '59', '37', '30', '10', '15', '11'] } df = pd.DataFrame(df)
现有Dash应用代码
from dash import Dash, html, dcc, dash_table, Input, Output import dash_bootstrap_components as dbc import plotly.express as px app = Dash(__name__, external_stylesheets= [dbc.themes.DARKLY]) app.layout = dbc.Container( dbc.Row([ dbc.Col([ #First Div, where objective of dashboard be presented #This piece also include our logo on left side #Few personalization options, simple template html.Div([ html.Img(id='logo_png', src=app.get_asset_url("logo.png"), height= 50 ), html.H5("Os fundos que acompanhamos estão dividos nas seguintes abaixo" ), #Phrase below our pie chart html.P('Selecione a classe que deseja obter mais informações' ), #Pie chart id created inside this .Div html.Div([ dcc.Graph(id = 'pie_class_chart', figure = {}, style={'width': '100%', 'display': 'inline-block', 'vertical-align': 'right', }) ]), dbc.Col( html.Div([ dash_table.DataTable( id = 'tabela-dash', columns = [{"id": i, "name": i} for i in df.columns], data= df.to_dict('records'), style_table={'height': '510px', 'overflowY': 'auto', 'display': 'inline-block', 'vertical-align':'left', 'float': 'left'}, style_data={ 'color': 'black', 'backgroundColor': 'white'}, page_size= 15 ) ]), ), #Dropdown of funds classification #Dropdown-value is the id necessary to insert possible values inside what we want # html.Div(id="fund_class_selector", children=[ html.P('Selecione abaixo o fundo para o qual deseja obter mais informações' ), dcc.Dropdown(df['Nome'].unique(), id= 'dropdown-value')], ) ]), #Options of windowns, with click buttons ]), ]), ) ####INTERACTIVE FUNCTIONS ARE BELOW###### ###### #Callback of first chart @app.callback( Output(component_id='pie_class_chart', component_property= 'figure'), Input(component_id='tabela-dash', component_property='data')) def update_graph(fund_class): fig = px.pie(df, values = 'número de fundos', names = 'tipo', title = 'Teste' ) return fig #callback of datatable values @app.callback( Output(component_id="dropdown-value", component_property='options'), Input(component_id = 'pie_class_chart', component_property='clickData')) def display_click_data(clickData): try: value = clickData['points'][0]['label'] dcc.Dropdown(df[df['tipo'] == value]['Nome']) return dcc.Dropdown except: print('error') if __name__ == '__main__': app.run_server(debug=True, use_reloader=False )
修正后的代码及逻辑说明
原代码问题分析
- 图表回调依赖表格数据,逻辑不合理:图表初始化无需依赖表格,且原计划用旭日图却误用了饼图
- 下拉菜单回调错误:返回
dcc.Dropdown组件而非下拉选项列表,列名拼写错误(用type替代了DataFrame中的tipo) - 布局中表格使用未定义变量,需统一为
df
完整修正代码
from dash import Dash, html, dcc, dash_table, Input, Output import dash_bootstrap_components as dbc import plotly.express as px import pandas as pd # 初始化DataFrame df = {'Nome': ['Aberdeen Multi Asset Growth Advisory FIM IE', 'Absolute Alpha Global Advisory FIC FIM', 'Absolute Alpha Marb Advisory FIC FIM', 'Absolute Hedge FIC FIM', 'Claritas Hedge Advisory FIC FIM Longo Prazo', 'MOS Institucional FIA', 'Occam Long Biased Advisory FIC FIM', 'Sharp Equity Value Advisory FIC FIA', 'Vinland Macro Advisory FIC FIM'], 'tipo':['Internacional Multimercado Hedgeado', 'Multiestratégia', 'Internacional Multimercado Hedgeado', 'Macro Média Vol', 'Macro Média Vol', 'Renda Variável Long Only', 'Renda Variável Long Biased', 'Renda Variável Long Only', 'Macro Média Vol'], 'retorno 1 mês':[ '0,79%', '0,38%', '0,40%', '0,38%', '0,82%', '4,11%', '4,02%', '4,55%', '0,72%'], 'classes' : [ 'Internacional Multimercado Hedgeado', 'Multiestratégia', 'Macro Média Vol', 'Renda Variável Long Only', 'Renda Variável Long Biased', 'Macro Alta Vol', 'Quantitativo', 'Long Short Direcional', 'Long Short Neutro'], 'número de fundos': ['8', '6', '10', '59', '37', '30', '10', '15', '11'] } df = pd.DataFrame(df) # 初始化Dash应用 app = Dash(__name__, external_stylesheets= [dbc.themes.DARKLY]) app.layout = dbc.Container( dbc.Row([ dbc.Col([ html.Div([ html.Img(id='logo_png', src=app.get_asset_url("logo.png"), height= 50), html.H5("Os fundos que acompanhamos estão dividos nas seguintes abaixo"), html.P('Selecione a classe que deseja obter mais informações'), # 旭日图容器 html.Div([ dcc.Graph(id = 'sunburst_class_chart', figure = {}, style={'width': '100%', 'display': 'inline-block', 'vertical-align': 'right'}) ]), dbc.Col( html.Div([ dash_table.DataTable( id = 'tabela-dash', columns = [{"id": i, "name": i} for i in df.columns], data= df.to_dict('records'), style_table={'height': '510px', 'overflowY': 'auto', 'display': 'inline-block', 'vertical-align':'left', 'float': 'left'}, style_data={'color': 'black', 'backgroundColor': 'white'}, page_size= 15 ) ]), ), html.Div(id="fund_class_selector", children=[ html.P('Selecione abaixo o fundo para o qual deseja obter mais informações'), dcc.Dropdown(options=df['Nome'].unique(), value=None, id= 'dropdown-value')], ) ]), ]), ]), ) # 生成旭日图的回调 @app.callback( Output(component_id='sunburst_class_chart', component_property= 'figure'), # 无输入,直接初始化图表 Input(component_id='sunburst_class_chart', component_property='id') ) def generate_sunburst(_): # 构建旭日图层级:classes -> tipo fig = px.sunburst(df, path=['classes', 'tipo'], values='número de fundos', title='Classificação de Fundos') return fig # 下拉菜单更新回调 @app.callback( Output(component_id="dropdown-value", component_property='options'), Input(component_id = 'sunburst_class_chart', component_property='clickData') ) def update_dropdown_options(clickData): # 默认显示所有基金 if not clickData: return [{'label': nome, 'value': nome} for nome in df['Nome'].unique()] # 获取点击的类别标签 clicked_label = clickData['points'][0]['label'] # 根据点击层级过滤对应基金 if clicked_label in df['tipo'].unique(): filtered_funds = df[df['tipo'] == clicked_label]['Nome'].unique() elif clicked_label in df['classes'].unique(): filtered_funds = df[df['classes'] == clicked_label]['Nome'].unique() else: filtered_funds = df['Nome'].unique() # 返回标准Dropdown选项格式 return [{'label': fund, 'value': fund} for fund in filtered_funds] if __name__ == '__main__': app.run_server(debug=True, use_reloader=False )
关键修正点
- 替换为旭日图:用
px.sunburst生成目标图表,通过path参数定义classes和tipo的层级结构 - 修正图表回调:移除不必要的表格输入,直接初始化旭日图
- 下拉菜单逻辑优化:
- 默认显示所有基金名称
- 识别点击的层级(大类/小类),精准过滤对应基金
- 返回符合Dash要求的选项格式(
label+value键值对)
- 布局变量修正:统一使用已定义的
df变量,修复未定义变量问题
内容的提问来源于stack exchange,提问作者GLVieira
相关产品推荐
相关产品推荐

