如何在Dash应用中绘制Sankey桑基图并解决回调返回值报错问题
报错修复方法
你遇到的dash.exceptions.InvalidCallbackReturnValue报错由两个问题引发:
- 布局中
id="output_container"的组件被你注释删除了,但回调中仍然保留了该输出项,Dash找不到对应接收值的组件 - 回调返回值顺序和声明的输出顺序不匹配:你声明的输出顺序是
[container内容, 图表figure],但函数返回的是(fig, container),值完全错位
业务逻辑修复
原有桑基图生成逻辑不符合需求,还需要做以下调整:
- 修复数据筛选逻辑:保留选中标签关联的数据,同时增加
value>=用户选择阈值的过滤条件 - 桑基图的source、target必须为节点列表的索引值,不能直接传入字符串标签
- 增加年份分层节点,实现不同年份的标签流向展示
完整可运行代码
import pandas as pd import plotly.graph_objects as go import dash_core_components as dcc import dash_html_components as html from dash.dependencies import Input, Output import numpy as np from dash import Dash app = Dash(__name__) # 直接嵌入数据集,无需读外部csv data = [ ["cs.AI",1.00,1990],["cs.AI",0.20,1990],["cs.AI",0.85,1990],["cs.AI",0.99,1990],["cs.AI",0.19,1990],["cs.AI",0.87,1990], ["cs.CC",0.19,1990],["cs.CC",1.00,1990],["cs.CC",0.34,1990],["cs.CC",0.50,1990],["cs.CC",0.09,1990],["cs.CC",0.67,1990], ["cs.CE",0.94,1990],["cs.CE",0.63,1990],["cs.CE",1.00,1990],["cs.CE",0.61,1990],["cs.CE",0.82,1990],["cs.CE",0.17,1990], ["cs.CG",0.74,1990],["cs.CG",0.95,1990],["cs.CG",0.53,1990],["cs.CG",1.00,1990],["cs.CG",0.43,1990],["cs.CG",0.10,1990], ["cs.CL",0.31,1990],["cs.CL",0.27,1990],["cs.CL",0.91,1990],["cs.CL",0.21,1990],["cs.CL",1.00,1990],["cs.CL",0.12,1990], ["cs.CR",0.31,1990],["cs.CR",0.18,1990],["cs.CR",0.76,1990],["cs.CR",0.35,1990],["cs.CR",0.67,1990],["cs.CR",1.00,1990], ["cs.AI",1.00,1991],["cs.AI",0.55,1991],["cs.AI",0.82,1991],["cs.AI",0.05,1991],["cs.AI",0.17,1991],["cs.AI",0.83,1991], ["cs.CC",0.52,1991],["cs.CC",1.00,1991],["cs.CC",0.64,1991],["cs.CC",1.00,1991],["cs.CC",0.80,1991],["cs.CC",0.21,1991], ["cs.CE",0.10,1991],["cs.CE",0.58,1991],["cs.CE",1.00,1991],["cs.CE",0.01,1991],["cs.CE",0.77,1991],["cs.CE",0.19,1991], ["cs.CG",0.08,1991],["cs.CG",0.21,1991],["cs.CG",0.63,1991],["cs.CG",1.00,1991],["cs.CG",0.34,1991],["cs.CG",0.60,1991], ["cs.CL",0.91,1991],["cs.CL",0.33,1991],["cs.CL",0.60,1991],["cs.CL",0.57,1991],["cs.CL",1.00,1991],["cs.CL",0.37,1991], ["cs.CR",1.00,1991],["cs.CR",0.28,1991],["cs.CR",0.92,1991],["cs.CR",0.47,1991],["cs.CR",0.53,1991],["cs.CR",1.00,1991], ["cs.AI",1.00,1992],["cs.AI",0.79,1992],["cs.AI",0.86,1992],["cs.AI",0.30,1992],["cs.AI",0.27,1992],["cs.AI",0.91,1992], ["cs.CC",0.06,1992],["cs.CC",1.00,1992],["cs.CC",0.72,1992],["cs.CC",0.44,1992],["cs.CC",0.31,1992],["cs.CC",0.75,1992], ["cs.CE",0.40,1992],["cs.CE",0.07,1992],["cs.CE",1.00,1992],["cs.CE",0.88,1992],["cs.CE",0.79,1992],["cs.CE",0.03,1992], ["cs.CG",0.74,1992],["cs.CG",0.91,1992],["cs.CG",1.00,1992],["cs.CG",1.00,1992],["cs.CG",0.68,1992],["cs.CG",0.22,1992], ["cs.CL",0.42,1992],["cs.CL",0.03,1992],["cs.CL",0.95,1992],["cs.CL",0.17,1992],["cs.CL",1.00,1992],["cs.CL",0.28,1992], ["cs.CR",0.04,1992],["cs.CR",0.30,1992],["cs.CR",0.26,1992],["cs.CR",0.80,1992],["cs.CR",0.90,1992],["cs.CR",1.00,1992] ] df = pd.DataFrame(data, columns=["Label1", "value", "year"]) app.layout = html.Div([ html.H1("标签年份流向桑基图", style={'text-align': 'center'}), dcc.Dropdown(id="slct_label", options=[{'label': x, 'value': x} for x in sorted(df["Label1"].unique())], multi=False, value="cs.AI", style={'width': "30%"} ), html.Br(), dcc.Dropdown(id="slct_value", options=[{'label': x, 'value': x} for x in sorted(df[df["value"] >= 0.75]["value"].unique())], multi=False, value=0.75, style={'width': "40%"}, placeholder="Select threshold" ), html.Div(id='output_container', children=[]), html.Br(), dcc.Graph(id='my_bee_map', figure={}) ]) @app.callback( [Output(component_id='output_container', component_property='children'), Output(component_id='my_bee_map', component_property='figure')], [Input(component_id='slct_label', component_property='value'), Input(component_id='slct_value', component_property='value')] ) def update_graph(slct_label, slct_value): container = "当前选中标签:{},阈值:{}".format(slct_label, slct_value) # 筛选符合条件的数据 dff = df[(df['Label1'] == slct_label) & (df['value'] >= slct_value)].copy() if dff.empty: return container, go.Figure() # 构建节点:格式为 年份_标签 nodes = [] for year in sorted(dff['year'].unique()): for label in [slct_label]: node_name = f"{year}_{label}" if node_name not in nodes: nodes.append(node_name) # 增加关联标签节点 for idx, row in dff.iterrows(): node_name = f"{row['year']}_关联标签" if node_name not in nodes: nodes.append(node_name) # 构建边 source = [] target = [] value = [] color = [] year_list = sorted(dff['year'].unique()) color_pool = ['#a6cee3','#fdbf6f','#fb9a99','#e3a6ce','#a6e3da'] for i in range(len(year_list)-1): y1 = year_list[i] y2 = year_list[i+1] # 同标签跨年流向 s_idx = nodes.index(f"{y1}_{slct_label}") t_idx = nodes.index(f"{y2}_{slct_label}") source.append(s_idx) target.append(t_idx) value.append(dff[dff['year'] == y1]['value'].sum()) color.append(color_pool[i%len(color_pool)]) # 当年关联标签流向 s_idx = nodes.index(f"{y1}_{slct_label}") t_idx = nodes.index(f"{y1}_关联标签") source.append(s_idx) target.append(t_idx) value.append(dff[dff['year'] == y1]['value'].sum() * 0.3) color.append(color_pool[i%len(color_pool)]) # 最后一年的关联标签 last_y = year_list[-1] s_idx = nodes.index(f"{last_y}_{slct_label}") t_idx = nodes.index(f"{last_y}_关联标签") source.append(s_idx) target.append(t_idx) value.append(dff[dff['year'] == last_y]['value'].sum() * 0.3) color.append(color_pool[-1]) # 生成桑基图 link = dict(source=source, target=target, value=value, color=color) node = dict(label=[n.split('_')[1] if '关联' not in n else n.split('_')[1] for n in nodes], pad=35, thickness=15) data = go.Sankey(link=link, node=node) fig = go.Figure(data) fig.update_layout( hovermode= 'x', title = f'{slct_label} 标签年份流向', font=dict(size=10,color='white'), paper_bgcolor='#51504f' ) # 注意返回顺序要和输出声明顺序一致 return container, fig if __name__ == '__main__': app.run_server(debug=True)
内容的提问来源于stack exchange,提问作者chuky pedro
相关产品推荐
相关产品推荐

