Halaman refresh Python Dash tidak mengupdate data sumber

Aug 20 2020

Saya telah menulis aplikasi garis petak dasar yang menarik data dari csv dan menampilkannya pada grafik. Anda kemudian dapat mengubah nilai pada aplikasi dan pembaruan grafik.

Namun, ketika saya menambahkan data baru ke csv (dilakukan sekali setiap hari) aplikasi tidak memperbarui data saat memuat ulang halaman.

Perbaikannya biasanya adalah Anda mendefinisikan Anda app.layoutsebagai fungsi, seperti yang diuraikan di sini (gulir ke bawah untuk pembaruan pada pemuatan halaman). Anda akan melihat dalam kode saya di bawah ini bahwa saya telah melakukannya.

Ini kode saya:

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)

Hal-hal yang telah saya coba

  1. Menghapus inisial df = pd.read_csv(path)sebelum def layout_function():. Ini menghasilkan kesalahan.
  2. Membuat tombol callback untuk menyegarkan data menggunakan kode ini:
@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')

Ini tidak menghasilkan kesalahan, tetapi tombolnya juga tidak menyegarkan data.

  1. Mendefinisikan dfdalam update_graphpanggilan balik. Ini memperbarui data setiap kali Anda beralih sesuatu, yang tidak praktis (data asli saya adalah> 10 ^ 6 baris, jadi saya tidak ingin membacanya setiap kali pengguna mengubah nilai toggle)

Singkatnya, saya pikir mendefinisikan app.layout = layout_functionharus membuat ini berhasil, tetapi ternyata tidak. Apa yang saya lewatkan / tidak lihat?

Hargai bantuan apapun.

Jawaban

3 emher Aug 21 2020 at 06:03

TLDR; Saya menyarankan agar Anda cukup memuat data dari dalam callback. Jika waktu buka terlalu lama, Anda dapat mengubah format (mis. Menjadi bulu ) dan / atau mengurangi ukuran data melalui pemrosesan awal. Jika ini masih belum cukup cepat, langkah selanjutnya adalah menyimpan data di cache dalam memori sisi server seperti Redis .


Karena Anda menugaskan ulang dfdan df2dalam layout_function, variabel ini dianggap lokal dalam Python , dan dengan demikian Anda tidak memodifikasi variabel dfdan df2dari ruang lingkup global. Meskipun Anda dapat mencapai perilaku ini menggunakan kata kunci global , penggunaan variabel global tidak disarankan di Dash .

Pendekatan standar di Dash akan memuat data dalam callback (atau di layout_function) dan menyimpannya dalam Storeobjek (atau yang sama, tersembunyi Div). Strukturnya akan seperti ini

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

Namun, pendekatan ini biasanya akan jauh lebih lambat daripada hanya membaca data dari disk di dalam callback (yang tampaknya menjadi apa yang Anda coba hindari) karena menghasilkan data yang ditransfer antara server dan klien.