【问题标题】:Python Dash refresh page not updating source dataPython Dash刷新页面不更新源数据
【发布时间】:2020-08-20 09:21:45
【问题描述】:

我编写了一个基本的 plotly dash 应用程序,它从 csv 中提取数据并将其显示在图表上。 然后,您可以在应用上切换值并更新图表。

但是,当我将新数据添加到 csv(每天完成一次)时,应用不会在刷新页面时更新数据。

解决方法通常是将app.layout 定义为函数,如here 所述(向下滚动到页面加载更新)。你会在下面的代码中看到我已经做到了。

这是我的代码:

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. 删除def layout_function(): 之前的初始df = pd.read_csv(path)。这会导致错误。
  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. update_graph 回调中定义df。每次切换时都会更新数据,这是不切实际的(我的真实数据是 > 10^6 行,所以我不想在每次用户更改切换值时都读取它)

简而言之,我认为定义app.layout = layout_function 应该可以实现这一点,但事实并非如此。我错过/没有看到什么?

感谢任何帮助。

【问题讨论】:

  • 是否有可能数据正在被缓存,而您只获得仍在(陈旧)缓存中的内容?我看不出你所做的有任何明显的问题。

标签: python plotly-dash


【解决方案1】:

TLDR;我建议您只需从回调中加载数据。如果加载时间过长,您可以更改格式(例如更改为feather)和/或通过预处理减小数据大小。如果这还不够快,下一步就是将数据存储在服务器端内存缓存中,例如Redis


由于您在layout_function 中重新分配dfdf2,因此这些变量被视为local in Python,因此您不会修改dfdf2 变量从全局范围。虽然您可以使用global keywordthe use of global variables is discouraged in Dash 来实现此行为。

Dash 中的标准方法是将数据加载到回调中(或layout_function)并将其存储在Store 对象中(或等效地隐藏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)
    ...

但是,这种方法通常会慢得多,因为它会导致数据在回调中传输服务器和客户端。

【讨论】:

  • 感谢您的详细解答。这有助于我了解发生了什么。从回调加载数据是可行的,但如前所述会减慢应用程序的速度。我将研究使用羽毛来查看这是否可以将其加速到可接受的水平,但是,就我的目的而言,在布局函数中使用 global 关键字会使应用程序的行为完全符合我的要求。我知道这是不好的做法,但是因为我只是用它来提取数据(而不是以任何特定于会话的方式更改它),我觉得这不会是一个问题。 (也暂时,我将是唯一的用户)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-29
  • 2014-04-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多