【问题标题】:Python Dash Update Dataframe every 60 MinutesPython Dash 每 60 分钟更新一次数据帧
【发布时间】:2020-08-12 11:00:06
【问题描述】:

我有一个脚本,它每小时收集一次数据并使用 Plotly Dash 将其可视化。

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd

df = pd.read_csv("/home/pi/backup/data/data.csv").sort_values(by="Number")
df["Datetime"] = df["Datetime"].str.replace("T", " ").str.replace("Z", "")
df["Datetime"] = pd.to_datetime(df["Datetime"], format="%Y-%m-%d %H:%M:%S")
df["Datetime"] = df["Datetime"].dt.floor("H")
grouped = df.groupby(["Datetime", "Number", "Shop"]).min().reset_index()

app = dash.Dash(__name__)

tabs_styles = {
    'height': '30px'
}
tab_style = {
    'borderBottom': '1px solid #d6d6d6',
    'padding': '6px',
    'fontWeight': 'bold'
}
tab_selected_style = {
    'borderTop': '1px solid #d6d6d6',
    'borderBottom': '1px solid #d6d6d6',
    'backgroundColor': '#119DFF',
    'color': 'white',
    'padding': '6px'
}

fig = px.line(grouped, x="Datetime", y="Price", hover_name="Price",
                     facet_col='Number', color="Shop", facet_col_wrap=5,
                    width=1900, height=850)
fig.update_yaxes(matches=None, title=None)
fig.update_xaxes(title=None)
fig.update_traces(line=dict(width=1))
fig.update_layout(transition_duration=500, hovermode="x unified")

app.layout = html.Div([
    dcc.Tabs([
         dcc.Tab(label ="Overview", children=[
            dcc.Graph(
                id='example-graph',
                figure=fig
            )
        ], style=tab_style, selected_style=tab_selected_style),
        dcc.Tab(label = "Detail", children=[
            dcc.Dropdown(id='Detail_Input', options=[
                {'label': i, 'value': i} for i in df.sort_values(by=["Number"])["Number"].unique()
                ], multi=False, value=df["Number"].min()),
        dcc.Graph(id="Detail_Graph"),
        ], style=tab_style, selected_style=tab_selected_style)
    ], style=tabs_styles)
])

@app.callback(
    Output("Detail_Graph", "figure"),
    [Input("Detail_Input", "value")])
def update_figure(input):
    fig = px.line(grouped[grouped["Number"] == input], x="Datetime", y="Price", color="Shop", hover_name="Price",
                  width=1900, height=850)
    fig.update_layout(transition_duration=500, hovermode="x unified")
    return fig

if __name__ == '__main__':
    app.run_server(debug=True,port=8050,host="0.0.0.0")

现在我想每小时更新一次数据框“df”。或者,我可以检查文件“/home/pi/backup/data/data.csv”是否已更新,如果是,请刷新数据。 在 google 或 stackoverflow 上找到了一些想法,但无法将其适应我的脚本(我对 Dash 很陌生......来自 R Shiny)。

【问题讨论】:

    标签: python plotly-dash


    【解决方案1】:

    我会建议以下方法,

    • 创建一个更新数据的回调 (A)。虽然您可以手动进行更新(即检查文件时间戳并根据需要进行更新),但使用超时的服务器端缓存(例如Flask-Caching)可能会更容易。

    • 创建第二个回调 (B),当回调 A 中的数据发生变化时绘制图形。

    如果你想要实时更新(即不刷新页面),你应该使用Interval 组件来触发回调A。这是一个使用dash_extensions==0.0.28 包的小例子,

    import dash_html_components as html
    import dash_core_components as dcc
    
    from datetime import datetime
    from dash_extensions.enrich import Dash, Trigger, Output, Input, ServersideOutput, FileSystemStore
    
    # Create server side store to hold the data.
    fss = FileSystemStore(cache_dir="some_dir", default_timeout=10)  # timeout in seconds, i.e. yours should be 3600
    # Create an example app.
    app = Dash(__name__)
    app.layout = html.Div([
        html.Div(id="log"),  # logging of data, a mock replacement of your graph
        dcc.Store(id="store"),  # store that holds the data reference
        dcc.Interval(id="trigger", interval=1000),  # trigger to invoke data refresh attempt, defaults to once per second
    ])
    
    
    @app.callback(ServersideOutput("store", "data", session_check=False, backend=fss),
                  Trigger("trigger", "n_intervals"), memoize=True)
    def update_data():
        return datetime.now()  # put your update logic here
    
    
    @app.callback(Output("log", "children"), Input("store", "data"))
    def show_data(data):
        return f"Data were collected at {data}, current time is {datetime.now()}"  # put your graph drawing code here
    
    
    if __name__ == "__main__":
        app.run_server()
    

    免责声明:我是dash_extensions的作者。

    【讨论】:

    • 感谢您采用这种方法。但我无法使其适应我的代码,所以我找到了另一个解决方案。
    • 我有一个基本问题,当两个人从不同位置打开网页时,应用程序是否显示来自服务器端商店的数据?还是第一次它会获取新数据,然后从下一小时开始使用服务器端存储?
    【解决方案2】:

    现在我找到了适合我的解决方案。不知道它是否是最佳实践。所以,如果有人有改进,我将不胜感激。

    不过,这只是一些 MB 的数据。性能不应该成为问题。

    import dash
    import dash_auth
    import dash_core_components as dcc
    import dash_html_components as html
    from dash.dependencies import Input, Output
    import plotly.express as px
    import pandas as pd
    
    
    df = pd.read_csv("/home/pi/backup/lego/log.csv")
    
    app = dash.Dash(__name__)
    auth = dash_auth.BasicAuth(
        app,
        VALID_USERNAME_PASSWORD_PAIRS
    )
    
    tabs_styles = {
        'height': '30px'
    }
    tab_style = {
        'borderBottom': '1px solid #d6d6d6',
        'padding': '6px',
        'fontWeight': 'bold'
    }
    tab_selected_style = {
        'borderTop': '1px solid #d6d6d6',
        'borderBottom': '1px solid #d6d6d6',
        'backgroundColor': '#119DFF',
        'color': 'white',
        'padding': '6px'
    }
    
    app.layout = html.Div([
        dcc.Tabs([
             dcc.Tab(label ="Overview", children=[
                dcc.Interval(id="interval", interval=60000), dcc.Graph(id="output")
            ], style=tab_style, selected_style=tab_selected_style),
            dcc.Tab(label = "Detail", children=[
                dcc.Dropdown(id='Detail_Input', options=[
                    {'label': i, 'value': i} for i in df.sort_values(by=["Number"])["Number"].unique()
                    ], multi=False, value=df["Number"].min()),
            dcc.Graph(id="Detail_Graph",
                    config={
                        'displayModeBar': False
                    }),
            ], style=tab_style, selected_style=tab_selected_style)
        ], style=tabs_styles)
    ])
    @app.callback(Output("output", "figure"), [Input("interval", "n_intervals")])
    def display_time(n):
        df = pd.read_csv("/home/pi/backup/lego/log.csv")
        df["Datetime"] = df["Datetime"].str.replace("T", " ").str.replace("Z", "")
        df["Datetime"] = pd.to_datetime(df["Datetime"], format="%Y-%m-%d %H:%M:%S")
        df["Datetime"] = df["Datetime"].dt.floor("H")
        grouped = df.groupby(["Datetime", "Number", "Shop"]).min().reset_index().sort_values(by=["Number", "Datetime"])
        fig = px.line(grouped, x="Datetime", y="Price", hover_name="Price",
                      facet_col='Number', color="Shop", facet_col_wrap=6,
                      width=1900, height=850)
        return fig
    @app.callback(
        Output("Detail_Graph", "figure"),
        [Input("Detail_Input", "value")])
    def update_figure(input):
        df = pd.read_csv("/home/pi/backup/lego/log.csv")
        df["Datetime"] = df["Datetime"].str.replace("T", " ").str.replace("Z", "")
        df["Datetime"] = pd.to_datetime(df["Datetime"], format="%Y-%m-%d %H:%M:%S")
        df["Datetime"] = df["Datetime"].dt.floor("H")
        grouped = df.groupby(["Datetime", "Number", "Shop"]).min().reset_index().sort_values(by=["Number", "Datetime"])
        fig = px.line(grouped[grouped["Number"] == input], x="Datetime", y="Price", color="Shop", hover_name="Price",
                      width=1900, height=850)
        fig.update_layout(transition_duration=500, hovermode="x unified")
        return fig
    
    if __name__ == '__main__':
        app.run_server(debug=True,port=8050,host="0.0.0.0")
    

    【讨论】:

      猜你喜欢
      • 2023-03-19
      • 2019-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-04-30
      • 1970-01-01
      • 2020-06-23
      • 2012-01-09
      相关资源
      最近更新 更多