Trang làm mới Python Dash không cập nhật dữ liệu nguồn

Aug 20 2020

Tôi đã viết một ứng dụng cơ bản về dấu gạch ngang có âm mưu lấy dữ liệu từ csv và hiển thị trên biểu đồ. Sau đó, bạn có thể chuyển đổi các giá trị trên ứng dụng và cập nhật biểu đồ.

Tuy nhiên, khi tôi thêm dữ liệu mới vào csv (thực hiện một lần mỗi ngày), ứng dụng không cập nhật dữ liệu khi làm mới trang.

Cách khắc phục thông thường là bạn xác định của mình app.layoutdưới dạng một hàm, như được nêu ở đây (cuộn xuống các bản cập nhật khi tải trang). Bạn sẽ thấy trong mã của tôi bên dưới rằng tôi đã làm điều đó.

Đây là mã của tôi:

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)

Những điều tôi đã thử

  1. Loại bỏ chữ cái đầu df = pd.read_csv(path)trước def layout_function():. Điều này dẫn đến một lỗi.
  2. Tạo nút gọi lại để làm mới dữ liệu bằng mã này:
@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')

Điều này không tạo ra lỗi, nhưng nút cũng không làm mới dữ liệu.

  1. Xác định dftrong cuộc update_graphgọi lại. Điều này cập nhật dữ liệu mỗi khi bạn chuyển đổi thứ gì đó, điều này không khả thi (dữ liệu thực của tôi> 10 ^ 6 hàng, vì vậy tôi không muốn đọc nó mỗi khi người dùng thay đổi giá trị chuyển đổi)

Tóm lại, tôi nghĩ rằng định nghĩa app.layout = layout_functionsẽ làm cho điều này hoạt động, nhưng nó không. Tôi đang thiếu / không nhìn thấy gì?

Đánh giá cao sự giúp đỡ.

Trả lời

3 emher Aug 21 2020 at 06:03

TLDR; Tôi khuyên bạn chỉ cần tải dữ liệu từ bên trong lệnh gọi lại. Nếu thời gian tải quá lâu, bạn có thể thay đổi định dạng (ví dụ: thành lông ) và / hoặc giảm kích thước dữ liệu thông qua xử lý trước. Nếu điều này vẫn chưa đủ nhanh, bước tiếp theo sẽ là lưu trữ dữ liệu trong bộ nhớ đệm trong bộ nhớ phía máy chủ như Redis .


Vì bạn đang chỉ định lại dfvà df2trong layout_function, các biến này được coi là cục bộ trong Python và do đó bạn không sửa đổi các biến dfvà df2từ phạm vi toàn cục. Mặc dù bạn có thể đạt được hành vi này bằng cách sử dụng từ khóa toàn cầu , nhưng việc sử dụng các biến toàn cục không được khuyến khích trong Dash .

Cách tiếp cận tiêu chuẩn trong Dash sẽ là tải dữ liệu trong một lệnh gọi lại (hoặc trong layout_function) và lưu trữ nó trong một Stoređối tượng (hoặc tương đương, một ẩn Div). Cấu trúc sẽ giống như

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)
    ...

Tuy nhiên, cách tiếp cận này thường sẽ chậm hơn nhiều so với việc chỉ đọc dữ liệu từ đĩa bên trong lệnh gọi lại (có vẻ là điều bạn đang cố gắng tránh) vì nó dẫn đến việc dữ liệu được chuyển giữa máy chủ và máy khách.