หน้ารีเฟรช Python Dash ไม่อัปเดตข้อมูลต้นฉบับ
ฉันได้เขียนแอปพล็อตพล็อตพื้นฐานที่ดึงข้อมูลจาก 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)
สิ่งที่ฉันพยายาม
- การลบค่าเริ่มต้น
df = pd.read_csv(path)
ก่อนdef layout_function():
. ส่งผลให้เกิดข้อผิดพลาด - การสร้างปุ่มโทรกลับเพื่อรีเฟรชข้อมูลโดยใช้รหัสนี้:
@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')
สิ่งนี้ไม่ก่อให้เกิดข้อผิดพลาด แต่ปุ่มจะไม่รีเฟรชข้อมูลด้วย
- การกำหนด
df
ภายในการupdate_graph
โทรกลับ สิ่งนี้จะอัปเดตข้อมูลทุกครั้งที่คุณสลับบางสิ่งซึ่งไม่สามารถทำได้ (ข้อมูลจริงของฉันคือ> 10 ^ 6 แถวดังนั้นฉันจึงไม่ต้องการอ่านทุกครั้งที่ผู้ใช้เปลี่ยนค่าการสลับ)
ในระยะสั้นฉันคิดว่าการกำหนดapp.layout = layout_function
ควรทำให้ได้ผล แต่มันไม่ได้ ฉันหายไป / ไม่เห็นอะไร
ขอบคุณสำหรับความช่วยเหลือใด ๆ
คำตอบ
TLDR; ฉันขอแนะนำให้คุณโหลดข้อมูลจากภายในการโทรกลับ หากเวลาในการโหลดนานเกินไปคุณสามารถเปลี่ยนรูปแบบ (เช่นขนนก ) และ / หรือลดขนาดข้อมูลผ่านการประมวลผลล่วงหน้า ถ้าเป็นแบบนี้คงไม่เร็วพอขั้นตอนต่อไปจะมีการเก็บข้อมูลในฝั่งเซิร์ฟเวอร์แคชในหน่วยความจำเช่นRedis
เนื่องจากคุณกำลังกำหนดใหม่df
และdf2
ในlayout_function
ตัวแปรเหล่านี้ถือว่าเป็นแบบโลคัลใน Pythonดังนั้นคุณจึงไม่ได้แก้ไขdf
และdf2
ตัวแปรจากขอบเขตส่วนกลาง ในขณะที่คุณสามารถบรรลุพฤติกรรมนี้โดยใช้คำหลักทั่วโลก , การใช้งานของตัวแปรทั่วโลกเป็นกำลังใจใน 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)
...
อย่างไรก็ตามวิธีนี้มักจะช้ากว่าการอ่านข้อมูลจากดิสก์ภายในการเรียกกลับ (ซึ่งดูเหมือนจะเป็นสิ่งที่คุณพยายามหลีกเลี่ยง) เนื่องจากส่งผลให้ข้อมูลถูกถ่ายโอนระหว่างเซิร์ฟเวอร์และไคลเอนต์