Dash多数据源Plotly图表仅最后一个正常,如何实现全部图表正常渲染?
问题:多数据源仪表盘仅最后一个图表正常渲染
我正在开发一款仪表盘,允许技术型终端用户添加数据源(以下用CSV文件模拟),目标是每个数据源对应生成一个图表,即3个数据源对应3个图表,以此类推。但当前代码仅对最后一个数据源生效,前2个数据源的图表为空白,仅第三个(实际为第二个,索引从0开始)按预期显示。
以下是简化后的实现代码:
import pandas as pd from dash import Dash, dcc, html, callback, Output, Input, State from datetime import date, datetime, timedelta from io import StringIO import csv pd.options.plotting.backend = "plotly" df0_str = '''"January","February","March","April","May","June","July","August","September","October","November","December" 1,3,4,2,5,6,5,2,1,3,4,6 2,4,5,3,6,7,6,3,2,4,5,7 3,5,6,4,7,8,7,4,3,5,6,8 4,6,7,5,8,9,8,5,4,6,7,9 5,7,8,6,9,10,9,6,5,7,8,10 6,8,9,7,10,11,10,7,6,8,9,11 7,9,10,8,11,12,11,8,7,9,10,12 8,10,11,9,12,13,12,9,8,10,11,13 9,11,12,10,13,14,13,10,9,11,12,14 10,12,13,11,14,15,14,11,10,12,13,15 11,13,14,12,15,16,15,12,11,13,14,16 12,14,15,13,16,17,16,13,12,14,15,17 13,15,16,14,17,18,17,14,13,15,16,18 14,16,17,15,18,19,18,15,14,16,17,19 15,17,18,16,19,20,19,16,15,17,18,20 16,18,19,17,20,21,20,17,16,18,19,21 17,19,20,18,21,22,21,18,17,19,20,22''' df1_str = '''"January","February","March","April","May","June","July","August","September","October","November","December" 10,13,14,13,10,9,11,12,14,1,3,4 13,14,12,15,16,15,12,11,13,2,4,5 14,15,13,16,17,16,13,12,14,3,5,6 15,16,14,17,18,17,14,13,15,4,6,7 16,17,15,18,19,18,15,14,16,5,7,8 17,18,16,19,20,19,16,15,17,6,8,9 18,19,17,20,21,20,17,16,18,7,9,10 19,20,18,21,22,21,18,17,19,8,10,11 13,9,11,2,5,6,5,2,1,3,4,6 14,16,12,3,6,7,6,3,2,4,5,7 15,17,13,4,7,8,7,4,3,5,6,8 16,18,14,5,8,9,8,5,4,6,7,9 17,19,15,6,9,10,9,6,5,7,8,10 18,20,16,7,10,11,10,7,6,8,9,11 19,21,17,8,11,12,11,8,7,9,10,12 20,22,,9,12,13,12,9,8,10,11,13''' df_list = [] dfstr_list = [df0_str, df1_str] for df_str in dfstr_list: csvStringIO = StringIO(df_str) df_list.append(pd.read_csv(csvStringIO, sep=",")) my_children = [] for num, df in enumerate(df_list): my_children.append(html.H3( children=f"name of {num}", )) my_children.append(dcc.Store(id=f'data_{num}', data=df.to_dict())) my_children.append(dcc.Input(id=f'range_{num}', type='number', value=12, min=1, step=1)) my_children.append(html.Div(dcc.Graph(id=f'fig_{num}', ))) # for child in my_children: # print(child) @callback( Output(f'fig_{num}', 'figure'), Input(f'range_{num}', 'value'), State(f'data_{num}', 'data') ) def update_graph(range_selected, data): df_for_figure = pd.DataFrame.from_dict(data).iloc[-range_selected:, :] fig = df_for_figure.plot() return fig app = Dash(__name__) app.layout = html.Div( children=my_children ) if __name__ == '__main__': app.run(debug=True)
问题原因
你当前只定义了一个回调函数,而这个回调里的num变量是循环结束后的最终值(这里是1,因为df_list有2个元素,循环时num从0到1,循环结束后保留1)。也就是说,这个回调只绑定了fig_1这个图表组件,而fig_0没有对应的回调函数来更新它,所以前一个图表始终是空白。
解决方案
需要为每个数据源动态生成对应的回调函数,可以在循环遍历数据源的时候,为每个num创建独立的回调。修改后的代码如下:
import pandas as pd from dash import Dash, dcc, html, callback, Output, Input, State from datetime import date, datetime, timedelta from io import StringIO import csv pd.options.plotting.backend = "plotly" df0_str = '''"January","February","March","April","May","June","July","August","September","October","November","December" 1,3,4,2,5,6,5,2,1,3,4,6 2,4,5,3,6,7,6,3,2,4,5,7 3,5,6,4,7,8,7,4,3,5,6,8 4,6,7,5,8,9,8,5,4,6,7,9 5,7,8,6,9,10,9,6,5,7,8,10 6,8,9,7,10,11,10,7,6,8,9,11 7,9,10,8,11,12,11,8,7,9,10,12 8,10,11,9,12,13,12,9,8,1</think_never_used_51bce0c785ca2f68081bfa7d91973934> <answer> # 问题:多数据源仪表盘仅最后一个图表正常渲染 我正在开发一款仪表盘,允许技术型终端用户添加数据源(以下用CSV文件模拟),目标是每个数据源对应生成一个图表,即3个数据源对应3个图表,以此类推。但当前代码仅对最后一个数据源生效,前2个数据源的图表为空白,仅第三个(实际为第二个,索引从0开始)按预期显示。 以下是简化后的实现代码: ```python import pandas as pd from dash import Dash, dcc, html, callback, Output, Input, State from datetime import date, datetime, timedelta from io import StringIO import csv pd.options.plotting.backend = "plotly" df0_str = '''"January","February","March","April","May","June","July","August","September","October","November","December" 1,3,4,2,5,6,5,2,1,3,4,6 2,4,5,3,6,7,6,3,2,4,5,7 3,5,6,4,7,8,7,4,3,5,6,8 4,6,7,5,8,9,8,5,4,6,7,9 5,7,8,6,9,10,9,6,5,7,8,10 6,8,9,7,10,11,10,7,6,8,9,11 7,9,10,8,11,12,11,8,7,9,10,12 8,10,11,9,12,13,12,9,8,10,11,13 9,11,12,10,13,14,13,10,9,11,12,14 10,12,13,11,14,15,14,11,10,12,13,15 11,13,14,12,15,16,15,12,11,13,14,16 12,14,15,13,16,17,16,13,12,14,15,17 13,15,16,14,17,18,17,14,13,15,16,18 14,16,17,15,18,19,18,15,14,16,17,19 15,17,18,16,19,20,19,16,15,17,18,20 16,18,19,17,20,21,20,17,16,18,19,21 17,19,20,18,21,22,21,18,17,19,20,22''' df1_str = '''"January","February","March","April","May","June","July","August","September","October","November","December" 10,13,14,13,10,9,11,12,14,1,3,4 13,14,12,15,16,15,12,11,13,2,4,5 14,15,13,16,17,16,13,12,14,3,5,6 15,16,14,17,18,17,14,13,15,4,6,7 16,17,15,18,19,18,15,14,16,5,7,8 17,18,16,19,20,19,16,15,17,6,8,9 18,19,17,20,21,20,17,16,18,7,9,10 19,20,18,21,22,21,18,17,19,8,10,11 13,9,11,2,5,6,5,2,1,3,4,6 14,16,12,3,6,7,6,3,2,4,5,7 15,17,13,4,7,8,7,4,3,5,6,8 16,18,14,5,8,9,8,5,4,6,7,9 17,19,15,6,9,10,9,6,5,7,8,10 18,20,16,7,10,11,10,7,6,8,9,11 19,21,17,8,11,12,11,8,7,9,10,12 20,22,,9,12,13,12,9,8,10,11,13''' df_list = [] dfstr_list = [df0_str, df1_str] for df_str in dfstr_list: csvStringIO = StringIO(df_str) df_list.append(pd.read_csv(csvStringIO, sep=",")) my_children = [] for num, df in enumerate(df_list): my_children.append(html.H3( children=f"name of {num}", )) my_children.append(dcc.Store(id=f'data_{num}', data=df.to_dict())) my_children.append(dcc.Input(id=f'range_{num}', type='number', value=12, min=1, step=1)) my_children.append(html.Div(dcc.Graph(id=f'fig_{num}', ))) # for child in my_children: # print(child) @callback( Output(f'fig_{num}', 'figure'), Input(f'range_{num}', 'value'), State(f'data_{num}', 'data') ) def update_graph(range_selected, data): df_for_figure = pd.DataFrame.from_dict(data).iloc[-range_selected:, :] fig = df_for_figure.plot() return fig app = Dash(__name__) app.layout = html.Div( children=my_children ) if __name__ == '__main__': app.run(debug=True)
问题原因
你当前只定义了一个回调函数,而这个回调里的num变量是循环结束后的最终值(这里是1,因为df_list有2个元素,循环时num从0到1,循环结束后保留1)。也就是说,这个回调只绑定了fig_1这个图表组件,而fig_0没有对应的回调函数来更新它,所以前一个图表始终是空白。
解决方案
需要为每个数据源动态生成对应的回调函数,可以在循环遍历数据源的时候,为每个num创建独立的回调。修改后的代码如下:
import pandas as pd from dash import Dash, dcc, html, callback, Output, Input, State from datetime import date, datetime, timedelta from io import StringIO import csv pd.options.plotting.backend = "plotly" df0_str = '''"January","February","March","April","May","June","July","August","September","October","November","December" 1,3,4,2,5,6,5,2,1,3,4,6 2,4,5,3,6,7,6,3,2,4,5,7 3,5,6,4,7,8,7,4,3,5,6,8 4,6,7,5,8,9,8,5,4,6,7,9 5,7,8,6,9,10,9,6,5,7,8,10 6,8,9,7,10,11,10,7,6,8,9,11 7,9,10,8,11,12,11,8,7,9,10,12 8,10,11,9,12,13,12,9,8,10,11,13 9,11,12,10,13,14,13,10,9,11,12,14 10,12,13,11,14,15,14,11,10,12,13,15 11,1
相关产品推荐
相关产品推荐

