【问题标题】:Callback error updating plot-div.children (Plotly Dash)回调错误更新 plot-div.children (Plotly Dash)
【发布时间】:2020-08-12 12:14:20
【问题描述】:

我遇到了一个奇怪的行为 - 我在 Plotly 论坛和 Stackoverflow 上看到了类似的问题,但没有解决方案。基本上,我试图将中间值(以在其他回调中重用)存储在隐藏的 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-callbackshttps://dash.plotly.com/performance

【问题讨论】:

    标签: python plotly plotly-dash


    【解决方案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-callbackshttps://dash.plotly.com/performance

    【讨论】:

      猜你喜欢
      • 2020-02-15
      • 2021-12-15
      • 2020-03-07
      • 1970-01-01
      • 2020-03-17
      • 2021-08-13
      • 2020-07-30
      • 2021-06-07
      • 2021-06-06
      相关资源
      最近更新 更多