2 votes

Erreur de callback lors de la mise à jour de plot-div.children (Plotly Dash)

J'ai rencontré un comportement étrange - je vois des questions similaires sur les forums de Plotly et sur Stackoverflow, mais pas de solution. En gros, j'essaie de stocker une valeur intermédiaire (à réutiliser dans d'autres callbacks) dans une div cachée 'data-storage-json', mais le callback qui l'a comme Input ne semble pas avoir lieu. Il n'y a pas d'erreur sur le backend. Sur le front-end, j'obtiens "Callback error updating plot-div.children" (qui est le composant spécifié comme Output).

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 est quelque chose comme ça :

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"],
     })

J'ai également inclus un exemple de code autonome dans une réponse ci-dessous

Versions du paquet :

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

UPDATE : En fin de compte, le problème semble être dû à la taille de la base de données. Hidden-div ou Store ne pouvait gérer que quelques centaines de lignes. J'ai donc décidé d'utiliser Flask Caching/Memoization : voir https://dash.plotly.com/sharing-data-between-callbacks o https://dash.plotly.com/performance

1voto

Tom Points 491

Le code (simplifié) ci-dessous fonctionne pour moi. Parce que vous n'avez pas fourni event_df Il n'est pas possible de voir quel est votre problème exact, mais je soupçonne que la 'id' en event_df n'est pas valide (par exemple, il ne commence pas à 0) et vous vous situez ici hors de portée :

selected_id=selected_id[0]
row=event_df.loc[selected_id,:]

Bien qu'il puisse s'agir d'un certain nombre d'autres problèmes. Si vous avez toujours des problèmes, vous pouvez peut-être fournir un échantillon. event_df DataFrame ?

Les versions des paquets sont également incluses à titre de référence

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

0voto

DAF Points 135

MISE À JOUR : j'ai inclus un exemple complet ci-dessous. Cet exemple utilise des données générées aléatoirement. Il fonctionne si, à la ligne 38, cinq minutes de données sont générées. Si dix minutes sont générées, j'obtiens l'erreur.

# -*- 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)

0voto

DAF Points 135

J'ai déterminé qu'il s'agissait de la taille du dataframe que j'essayais de stocker dans hidden-div (il n'en fallait pas beaucoup pour provoquer l'erreur). J'ai également essayé d'utiliser dcc.Store et j'ai observé le même comportement. Je suis donc passé à l'utilisation de Flask Caching/Memoization : voir https://dash.plotly.com/sharing-data-between-callbacks o https://dash.plotly.com/performance

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X