Reputation: 3294
I have a script which is collecting Data every hour and visualize this with 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")
Now I want to update the Dataframe "df" every hour. Alternatively I could check if the file "/home/pi/backup/data/data.csv" is updated and if yes, refresh the data. Found some ideas on google or stackoverflow, but was not able to adapt it to my script (I'm very new to Dash... coming from R Shiny).
Upvotes: 0
Views: 4832
Reputation: 3294
Now I found a solution that works for me. Don't know if its best practice. So, if someone has an improvement I'd be grateful.
Nevertheless, it's just some MB of data. Performance shouldn't become a problem.
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")
Upvotes: 0
Reputation: 6024
I would suggest the following approach,
Create one callback (A) that updates the data. While you could do the update manually (i.e. checking the file time stamp and doing updates as needed), it would probably be easier to use a server side cache (such as Flask-Caching) with a timeout instead.
Create a second callback (B) that draws the graphs when the data in callback A changes.
If you want live updates (i.e. without refreshing the page), you should use an Interval
component to trigger callback A. Here is a small example using the dash_extensions==0.0.28
package,
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()
Disclaimer: I am the author of dash_extensions
.
Upvotes: 5