回调错误更新 plot-div.children (Plotly Dash)
Posted
技术标签:
【中文标题】回调错误更新 plot-div.children (Plotly Dash)【英文标题】:Callback error updating plot-div.children (Plotly Dash) 【发布时间】:2020-08-12 12:14:20 【问题描述】:我遇到了一个奇怪的行为 - 我在 Plotly 论坛和 *** 上看到了类似的问题,但没有解决方案。基本上,我试图将中间值(以在其他回调中重用)存储在隐藏的 div 'data-storage-json' 中,但是将其作为输入的回调似乎没有发生。后端没有错误。在前端我得到“回调错误更新 plot-div.children”(这是指定为输出的组件)
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.exceptions import PreventUpdate
########### Layout:
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(children=[
html.Div(id='data-storage-json', style='display': 'none'),
html.Div(children=[
dash_table.DataTable(
id='event-table',
style_data='whiteSpace': 'normal', #'border': '1px solid blue',
style_cell='textAlign': 'center',
#style_header= 'border': '1px solid pink' ,
css=[
'selector': '.dash-cell div.dash-cell-value',
'rule': 'display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;'
],
columns=["name": i, "id": i for i in event_df.columns if i is not 'id'],
style_table='overflowX': 'scroll',
row_selectable='single',
selected_rows=[],
page_current=0,
page_size=PAGE_SIZE,
page_action='custom',
filter_action='custom',
filter_query='',
sort_action='custom',
sort_mode='multi',
sort_by=[]
),
html.Div(id='event-stats', style='width': '80%', 'color': 'black', 'font-size': '9')],
style='width': '90%', 'margin-left': '20px', 'font-size': '9', 'horizontal-align': 'middle', 'vertical-align': 'middle'),
html.Div(children=[html.Br()]),
html.Button('Plot', id='show-button'),
html.Div(id='plot-div', children=[], style='width': '95%', 'font-size': '9', 'vertical-align': 'middle'),
])
########### Callbacks:
'''
Callback for sorting/filtering table
'''
@app.callback(
[Output('event-table', 'data'),
Output('event-table', 'page_count'),
Output('event-stats', 'children')],
[Input('event-table', 'sort_by'),
Input('event-table', 'filter_query'),
Input('event-table', 'page_current'),
Input('event-table', 'page_size')])
def update_event_selection(sort_by, filter_query,page_current, page_size):
dff = sort_filter_table(event_df, filter_query, sort_by)
res = dff.iloc[page_current*page_size: (page_current + 1)*page_size]
page_count = int(dff.shape[0]/page_size)+1
stat_str = ' events in the table. Displaying page of '.format(dff.shape[0], page_current+1, page_count)
return res.to_dict('records'), page_count, stat_str
@app.callback(
Output('data-storage-json','children'),
[Input('show-button', 'n_clicks')],
[State('event-table','selected_row_ids')
])
def prepare_data(n_clicks,selected_id):
duration=1
print('Selected id: ',selected_id)
if n_clicks is None or selected_id is None or len(selected_id)==0:
raise PreventUpdate
duration=int(duration)
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
print(row)
event_time=pd.to_datetime(row['Start'],errors='ignore')
# sensors to load:
flist=['ip_m','vp_m','f','df']
print('Duration '.format(duration))
res_df=get_event_data(interconnect,event_time,duration, feature_list=flist)
print(res_df.shape)
js=res_df.to_json(date_format='iso', orient='split')
print('In Prep: ',len(js))
return js
@app.callback(
Output('plot-div','children'),
[Input('data-storage-json','children')],
[State('event-table','selected_row_ids')])
def generate_plots(data_storage,selected_id):
if data_storage is None:
print('None!!!')
raise PreventUpdate
else:
print('InDisplay -storage: '+str(len(data_storage)))
res_df = pd.read_json(data_storage, orient='split')
print('InDisplay ',res_df.shape)
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
event_time=pd.to_datetime(row['Start'],errors='ignore')
event_type=row['Event']+': '+row['Cause']
event_pid=''
# columns sorted in reverse alphabetical
flist=sorted(np.unique([c.split('__')[1] for c in res_df.columns]))[::-1]
print('To plot: ',res_df.shape)
# generate plots for each type of sensor:
fig_list=[]
for feature in flist:
col_list = [c for c in res_df.columns if not c.startswith('_') and c.endswith('_'+feature)]
temp_df = res_df[col_list]
# plot results
print('Preparing figure '+feature)
fig=temp_df.iplot(kind='scatter',mode='markers',size=3, title="Plot : ".format(feature,event_time,event_type,event_pid), asFigure=True)
#fig_list.append(fig)
fig_list.append((html.Div(children=[dcc.Graph(id=feature+'-scatter',figure=fig)])))
print('Figure done')
return fig_list
########### Run the app:
if __name__ == '__main__':
parser = argparse.ArgumentParser()
parser.add_argument('--gpu', type=int, default=0, help='number of GPU to use for calculations')
parser.add_argument('--port', type=int, default=8050, help='port on which to run (default: 8050)')
options,_ = parser.parse_known_args()
os.environ['CUDA_VISIBLE_DEVICES'] = str(options.gpu)
app.run_server(debug=True, port = options.port)
UPD:event_df 有点像:
event_df = pd.DataFrame("id": [0,1,2],
"Start": ["2016-01-01 14:33","2016-01-01 16:45","2016-01-01 17:46"],
"Event": ["Line Outage","Line Outage","Line Outage"],
)
我还在下面的答案中包含了一个独立的代码示例
包版本:
dash 1.8.0 py_0 conda-forge
dash-core-components 1.7.0 py_0 conda-forge
dash-html-components 1.0.2 py_0 conda-forge
dash-renderer 1.2.3 py_0 conda-forge
dash-table 4.6.0 py_0 conda-forge
更新: 最终问题似乎是由于数据框的大小。 Hidden-div 或 Store 只能处理几百行。所以我转而使用 Flask Caching/Memoization:见 https://dash.plotly.com/sharing-data-between-callbacks 或 https://dash.plotly.com/performance
【问题讨论】:
【参考方案1】:下面的(简化的)代码对我有用。因为您没有提供event_df
,所以无法查看您的确切问题,但我怀疑event_df
中的'id'
无效(例如不是从0 开始)并且您的地址超出范围这里:
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
虽然它可能是任何数量的其他问题。如果您仍然有问题,也许您可以提供一个示例event_df
DataFrame?
还包括供参考的软件包版本
import dash
import pandas as pd
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.exceptions import PreventUpdate
########### Layout:
app = dash.Dash(__name__)
event_df = pd.DataFrame("id": [0,1,2], "a": [11,21,31], "b": [41,51,61])
PAGE_SIZE=1
app.layout = html.Div(children=[
html.Div(id='data-storage-json', style='display': 'none'),
html.Div(children=[
dash_table.DataTable(
id='event-table',
style_data='whiteSpace': 'normal', #'border': '1px solid blue',
style_cell='textAlign': 'center',
#style_header= 'border': '1px solid pink' ,
css=[
'selector': '.dash-cell div.dash-cell-value',
'rule': 'display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;'
],
columns=["name": i, "id": i for i in event_df.columns if i is not 'id'],
style_table='overflowX': 'scroll',
row_selectable='single',
selected_rows=[],
page_current=0,
page_size=PAGE_SIZE,
page_action='custom',
filter_action='custom',
filter_query='',
sort_action='custom',
sort_mode='multi',
sort_by=[]
),
html.Div(id='event-stats', style='width': '80%', 'color': 'black', 'font-size': '9')],
style='width': '90%', 'margin-left': '20px', 'font-size': '9', 'horizontal-align': 'middle', 'vertical-align': 'middle'),
html.Div(children=[html.Br()]),
html.Button('Plot', id='show-button'),
html.Div(id='plot-div', children=[], style='width': '95%', 'font-size': '9', 'vertical-align': 'middle'),
])
########### Callbacks:
'''
Callback for sorting/filtering table
'''
@app.callback(
Output('event-table', 'data'),
[Input('event-table', 'sort_by'),
Input('event-table', 'filter_query'),
Input('event-table', 'page_current'),
Input('event-table', 'page_size')])
def update_event_selection(sort_by, filter_query,page_current, page_size):
return event_df.to_dict('records')
@app.callback(
Output('data-storage-json','children'),
[Input('show-button', 'n_clicks')],
[State('event-table','selected_row_ids')
])
def prepare_data(n_clicks,selected_id):
duration=1
print('Selected id: ',selected_id)
if n_clicks is None or selected_id is None or len(selected_id)==0:
raise PreventUpdate
duration=int(duration)
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
print(row)
res_df = pd.DataFrame("id": [0,1,2], "a": [11,21,31], "b": [41,51,61])
js=res_df.to_json(date_format='iso', orient='split')
print('In Prep: ',len(js))
return js
@app.callback(
Output('plot-div','children'),
[Input('data-storage-json','children')],
[State('event-table','selected_row_ids')])
def generate_plots(data_storage,selected_id):
if data_storage is None:
print('None!!!')
raise PreventUpdate
else:
print('InDisplay -storage: '+str(len(data_storage)))
res_df = pd.read_json(data_storage, orient='split')
print('InDisplay ',res_df.shape)
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
event_time=pd.to_datetime(row['Start'],errors='ignore')
event_type=row['Event']+': '+row['Cause']
event_pid=''
# columns sorted in reverse alphabetical
flist=sorted(np.unique([c.split('__')[1] for c in res_df.columns]))[::-1]
print('To plot: ',res_df.shape)
# generate plots for each type of sensor:
fig_list=[]
for feature in flist:
col_list = [c for c in res_df.columns if not c.startswith('_') and c.endswith('_'+feature)]
temp_df = res_df[col_list]
# plot results
print('Preparing figure '+feature)
fig=temp_df.iplot(kind='scatter',mode='markers',size=3, title="Plot : ".format(feature,event_time,event_type,event_pid), asFigure=True)
#fig_list.append(fig)
fig_list.append((html.Div(children=[dcc.Graph(id=feature+'-scatter',figure=fig)])))
print('Figure done')
return fig_list
########### Run the app:
if __name__ == '__main__':
app.run_server(debug=True)
Running on http://127.0.0.1:8050/
Debugger PIN: 361-595-854
Selected id: None
Selected id: [2]
id 2
a 31
b 61
Name: 2, dtype: int64
In Prep: 81
InDisplay -storage: 81
InDisplay (3, 3)
# Name Version Build Channel
dash 1.4.0 py_0 conda-forge
dash-bootstrap-components 0.8.1 py36_0 conda-forge
dash-core-components 1.3.0 py_0 conda-forge
dash-html-components 1.0.1 py_0 conda-forge
dash-renderer 1.1.1 py_0 conda-forge
dash-table 4.4.0 py_0 conda-forge
【讨论】:
感谢您试一试。我试过了,它有效。问题是"为什么? 其实现在我的版本也可以了,没有任何改动。就好像某些东西因多次运行/重新启动而搞砸了,现在又恢复正常了。 它不再工作了。我真的开始认为这与浏览器或服务器有关。 你的 Dash 包的版本还是一样的吗? 是的,还是一样。顺便说一句,我现在有一个更好的独立代码版本。将添加为另一个答案【参考方案2】:更新:我在下面提供了一个完整的示例。此示例使用随机生成的数据。如果在第 38 行生成了 5 分钟的数据,它就可以工作。如果生成十分钟,我会收到错误消息。
# -*- coding: utf-8 -*-
import dash
from dash.dependencies import Input, Output, State
import dash_core_components as dcc
import dash_html_components as html
import dash_table
from dash.exceptions import PreventUpdate
external_stylesheets = ['https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css',
'https://cdnjs.cloudflare.com/ajax/libs/vis/4.21.0/vis.min.css',
'https://codepen.io/chriddyp/pen/bWLwgP.css']
import numpy as np
import pandas as pd
from functools import reduce
import cufflinks as cf
from datetime import datetime as dt
import os
import sys
import argparse
#import plotly.offline
########### Prepare Data
PAGE_SIZE = 10
event_df = pd.DataFrame("id": [0,1,2],
"Start": ["2016-01-01 14:33","2016-01-01 16:45","2016-01-01 17:46"],
"Event": ["Line Outage","Line Outage","Line Outage"],
"Cause": ['','','']
)
def list2dict(l):
return ['label': x, 'value':x for x in l]
def make_random_data():#(useDates=True):
#if useDates:
date_rng = pd.date_range(start='1/01/2018 05:00:00', end='1/01/2018 05:05:00', freq='1S')
#else:
# date_rng = pd.Series([10, 20, 30, 40, 50])
df = pd.DataFrame(date_rng, columns=['date'])
cols=['A__ip_m','B__ip_m','A__vp_m','B__vp_m']
for c in cols:
df[c] = np.random.randint(0,100,size=(len(date_rng)))
df=df.set_index('date')
return df
########### Layout:
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.layout = html.Div(children=[
html.Div(id='data-storage-json', style='display': 'none'),
html.Div(children=[
dash_table.DataTable(
id='event-table',
data=event_df.to_dict('records'),
style_data='whiteSpace': 'normal',
style_cell='textAlign': 'center',
css=[
'selector': '.dash-cell div.dash-cell-value',
'rule': 'display: inline; white-space: inherit; overflow: inherit; text-overflow: inherit;'
],
columns=["name": i, "id": i for i in event_df.columns if i is not 'id'],
style_table='overflowX': 'scroll',
row_selectable='single',
selected_rows=[]
)]),
html.Div(children=[html.Br()]),
html.Button('Plot', id='show-button'),
html.Div(id='plot-div', children=[], style='width': '95%', 'font-size': '9', 'vertical-align': 'middle'),
])
########### Callbacks:
#Output('data-storage-json','children'),
# Output('plot-div','children'),
@app.callback(
Output('data-storage-json','children'),
[Input('show-button', 'n_clicks')],
[State('event-table','selected_row_ids')])
def prepare_data(n_clicks,selected_id):
if n_clicks is None or selected_id is None or len(selected_id)==0:
raise PreventUpdate
duration=1
selected_id=selected_id[0]
row=event_df.loc[selected_id,:]
print(row)
event_time=pd.to_datetime(row['Start'],errors='ignore')
res_df = make_random_data()#useDates=True)
print(res_df.shape)
print(res_df.head())
js=res_df.to_json(date_format='iso', orient='split') #date_format='epoch'
#res_df.to_json('epoch-sample.json',date_format='epoch', orient='split')
#res_df.to_json('iso-sample.json',date_format='iso', orient='split')
print('In Prep: ',len(js))
return js
@app.callback(
Output('plot-div','children'),
[Input('data-storage-json','children')])
def generate_plots(data_storage):
if data_storage is None:
print('None!!!')
raise PreventUpdate
else:
print('InDisplay -storage: '+str(len(data_storage)))
res_df = pd.read_json(data_storage, orient='split')
# columns sorted in reverse alphabetical
flist=sorted(np.unique([c.split('__')[1] for c in res_df.columns]))[::-1]
print('To plot: ',res_df.shape)
# generate plots for each type of sensor:
fig_list=[]
for feature in flist:
col_list = [c for c in res_df.columns if not c.startswith('_') and c.endswith('_'+feature)]
temp_df = res_df[col_list]
# plot results
print('Preparing figure '+feature)
fig=temp_df.iplot(kind='scatter',mode='markers',size=3, title="Plot", asFigure=True)
fig_list.append((html.Div(children=[dcc.Graph(id=feature+'-scatter',figure=fig)])))
print('Figure done')
return fig_list
########### Run the app:
if __name__ == '__main__':
app.run_server(debug=True)
【讨论】:
get_event_data 是否返回一个 DataFrame?我想这一定是数据的问题,而没有看到它很难知道它是什么 看来问题出在所存储数据的大小上:当我生成 300 行的数据框时,一切正常;但 600 则不会(注意:我将索引设置为日期字段)。 顺便说一句,我尝试使用 dcc.Store 而不是 hidden-div,但遇到了完全相同的问题【参考方案3】:我将其固定为我试图存储在 hidden-div 中的数据框的大小。 (导致错误并没有花费太多时间)。我也尝试使用 dcc.Store 并观察到相同的行为。所以我转而使用 Flask Caching/Memoization:见 https://dash.plotly.com/sharing-data-between-callbacks 或 https://dash.plotly.com/performance
【讨论】:
以上是关于回调错误更新 plot-div.children (Plotly Dash)的主要内容,如果未能解决你的问题,请参考以下文章
为啥 setState 回调会抛出错误:“来自 useState() 和 useReducer() Hooks 的状态更新不支持第二个回调参数...”
更新 Visual Studio 2017,现在出现编译错误 C7510:“回调”:使用依赖模板名称必须以“模板”为前缀
Plotly Dash dcc.Interval 在一段时间后失败:回调错误更新 graph.figure
我收到此错误:“来自 useState() 和 useReducer() Hooks 的状态更新不支持第二个回调...”当我更改状态时