Python Dash 새로 고침 페이지가 소스 데이터를 업데이트하지 않음

Aug 20 2020

csv에서 데이터를 가져와 차트에 표시하는 기본 플롯 대시 앱을 작성했습니다. 그런 다음 앱 및 그래프 업데이트에서 값을 전환 할 수 있습니다.

그러나 csv에 새 데이터를 추가하면 (하루에 한 번 수행) 앱이 페이지를 새로 고칠 때 데이터를 업데이트하지 않습니다.

수정은 일반적으로 여기에app.layout 설명 된대로 함수로 정의하는 것입니다 (페이지로드시 업데이트로 스크롤 다운). 아래 코드에서 내가 한 것을 볼 수 있습니다.

내 코드는 다음과 같습니다.

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import numpy as np

import pandas as pd

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

path = 'https://raw.githubusercontent.com/tbuckworth/Public/master/CSVTest.csv'

df = pd.read_csv(path)
df2 = df[(df.Map==df.Map)]


def layout_function():

    df = pd.read_csv(path)
    df2 = df[(df.Map==df.Map)]
    
    available_strats = np.append('ALL',pd.unique(df2.Map.sort_values()))
    classes1 = pd.unique(df2["class"].sort_values())
    metrics1 = pd.unique(df2.metric.sort_values())
    
    return html.Div([
            html.Div([
                dcc.Dropdown(
                    id="Strategy",
                    options=[{"label":i,"value":i} for i in available_strats],
                    value=list(available_strats[0:1]),
                    multi=True
                ),
                dcc.Dropdown(
                    id="Class1",
                    options=[{"label":i,"value":i} for i in classes1],
                    value=classes1[0]
                ),
                dcc.Dropdown(
                    id="Metric",
                    options=[{"label":i,"value":i} for i in metrics1],
                    value=metrics1[0]
                )],
            style={"width":"20%","display":"block"}),
                
        html.Hr(),
    
        dcc.Graph(id='Risk-Report')          
    ])
            
app.layout = layout_function


@app.callback(
        Output("Risk-Report","figure"),
        [Input("Strategy","value"),
         Input("Class1","value"),
         Input("Metric","value"),
         ])

def update_graph(selected_strat,selected_class,selected_metric):
    if 'ALL' in selected_strat:
        df3 = df2[(df2["class"]==selected_class)&(df2.metric==selected_metric)]
    else:
        df3 = df2[(df2.Map.isin(selected_strat))&(df2["class"]==selected_class)&(df2.metric==selected_metric)]
    df4 = df3.pivot_table(index=["Fund","Date","metric","class"],values="value",aggfunc="sum").reset_index()
    traces = []
    for i in df4.Fund.unique():
        df_by_fund = df4[df4["Fund"] == i]
        traces.append(dict(
                x=df_by_fund["Date"],
                y=df_by_fund["value"],
                mode="lines",
                name=i
                ))
    
    if selected_class=='USD':
        tick_format=None
    else:
        tick_format='.2%'
    
    return {
            'data': traces,
            'layout': dict(
                xaxis={'type': 'date', 'title': 'Date'},
                yaxis={'title': 'Values','tickformat':tick_format},
                margin={'l': 40, 'b': 40, 't': 10, 'r': 10},
                legend={'x': 0, 'y': 1},
                hovermode='closest'
            )
        }
    

if __name__ == '__main__':
    app.run_server(debug=True)

내가 시도한 것

  1. 초기 제거 df = pd.read_csv(path)전과를 def layout_function():. 이로 인해 오류가 발생합니다.
  2. 다음 코드를 사용하여 데이터를 새로 고치는 콜백 버튼 만들기 :
@app.callback(
        Output('Output-1','children'),
        [Input('reload_button','n_clicks')]        
        )

def update_data(nclicks):
    if nclicks == 0:
        raise PreventUpdate
    else:
        df = pd.read_csv(path)
        df2 = df[(df.Map==df.Map)]
        return('Data refreshed. Click to refresh again')

이것은 오류를 생성하지 않지만 버튼도 데이터를 새로 고치지 않습니다.

  1. 콜백 df내에서 정의 update_graph. 이것은 실행 불가능한 무언가를 토글 할 때마다 데이터를 업데이트합니다 (실제 데이터는> 10 ^ 6 행이므로 사용자가 토글 값을 변경할 때마다 데이터를 읽고 싶지 않습니다).

요컨대, 정의 app.layout = layout_function가이 작업을 수행해야 한다고 생각 하지만 그렇지 않습니다. 내가 무엇을 놓치고 있거나 보지 못합니까?

도움을 주셔서 감사합니다.

답변

3 emher Aug 21 2020 at 06:03

TLDR; 콜백 내에서 데이터를로드하는 것이 좋습니다. 로드 시간이 너무 긴 경우 형식 (예 : feather ) 을 변경 하거나 사전 처리를 통해 데이터 크기를 줄일 수 있습니다. 이것이 여전히 충분히 빠르지 않은 경우 다음 단계는 Redis 와 같은 서버 측 메모리 내 캐시에 데이터를 저장하는 것 입니다.


dfdf2에서 재 할당하기 때문에 layout_function이러한 변수는 Python 에서 로컬로 간주 되므로 전역 범위에서 및 변수를 수정 하지 않습니다 . 당신이 사용하는이 동작을 얻을 수 있지만 글로벌 키워드를 , 전역 변수의 사용은 대시에 권장되지 않습니다 .dfdf2

Dash의 표준 접근 방식은 콜백 (또는)에 데이터를로드하고 layout_function이를 Store객체 (또는 동등하게 hidden Div)에 저장하는 것입니다 . 구조는 다음과 같습니다.

import pandas as pd
import dash_core_components as dcc
from dash.dependencies import Output, Input

app.layout = html.Div([
    ...
    dcc.Store(id="store"), html.Div(id="trigger")
])

@app.callback(Output('store','data'), [Input('trigger','children')], prevent_initial_call=False)
def update_data(children):
    df = pd.read_csv(path)
    return df.to_json()

@app.callback(Output("Risk-Report","figure"), [Input(...)], [State('store', 'data')])
def update_graph(..., data):
    if data is None:
        raise PreventUpdate
    df = pd.read_json(data)
    ...

그러나이 접근 방식은 일반적으로 서버와 클라이언트간에 데이터가 전송되므로 콜백 내부의 디스크에서 데이터를 읽는 것보다 훨씬 느립니다 (피하려고하는 것처럼 보입니다).