I'm not clear how I could dynamically create multiple charts at once - or if that is not possible then how I could loop through a list of values using a single callback.
For example in the code below list of continents is a a list of filter options. Is it possible to basically make it so when this page loads, I see 5 charts automatically?
Currently, what I'm doing is I have to type 5 #app.callback...make_scatter_plot(option=_dropdown_value) which ends up with a million lines of code in my file and makes it hard to read even though everything is doing the same thing.
What am I missing? Thanks
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import pandas as pd
import numpy as np
app = Dash(__name__)
df = px.data.gapminder()
list_of_continents = ["Asia", "Africa", "Europe", 'Oceania', 'Americas']
app.layout = html.Div([
html.H4('Restaurant tips by day of week'),
dcc.Dropdown(
id="dropdown",
options=list_of_continents,
multi=False
),
dcc.Graph(id="graph"),
#dcc.Graph(id ='graph2') ##????
])
#app.callback(
Output("graph", "figure"),
Input("dropdown", "value")
)
def make_scatter_plot( value =[i for i in list_of_continents], df = df):
"""
"""
data = df[df['continent'].isin([value])]
fig = px.scatter(data, x="lifeExp", y="gdpPercap",
size="pop")
return fig
if __name__ == '__main__':
app.run_server(debug=True)
although plotly express can help you set up a graph with just one line of code it’s not very handy when it comes to customizing each trace. So, for that, you’ve to switch to graphs_objects.
In the following lines of code, the callback generates a Graph component for each trace and appends each graph component to a Div component. Hence you get multiple graphs using a single callback.
from dash import Dash, dcc, html, Input, Output
import plotly.express as px
import plotly.graph_objects as go
import pandas as pd
import numpy as np
app = Dash(__name__)
df = px.data.gapminder()
app.layout = html.Div([
html.H4('Restaurant tips by day of week'),
html.Div(id='graphs',children=[])
])
#app.callback(
Output("graphs", "children"),
Input("graphs", "children")
)
def make_scatter_plot(child):
"""
"""
for continent in df['continent'].unique():
df_filtered = df[df['continent'] == continent]
fig = go.Figure()
fig.add_trace(
go.Scatter(x = df_filtered['lifeExp'],
y = df_filtered['gdpPercap'],
mode = 'markers',
marker = dict(size = 10 + (df_filtered['pop'] - df_filtered['pop'].min()) * 20
/ (df_filtered['pop'].max() - df_filtered['pop'].min())) # This is just to scale the marker size value between 10 and 20.
)
)
fig.update_layout(
title_text = continent
)
child.append(dcc.Graph(figure=fig))
return child
if __name__ == '__main__':
app.run_server(debug=True)
The output of the Code:
Click here
Related
I want to draw a chart of btc and show one hour after one hour like a slideshow (no animation or effect is needed). However I'm not able to update the figure stemming from px.line() without the browser opening a new tab. How would I do this, like remaining in the same tab and just updating the data of the line and redraw it?
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import time
df = pd.read_csv('btc.csv')
i=0
shown=False
part_df1=df.iloc[0:60]
fig = px.line(part_df1, x = 'date_x', y = 'price_y', title='btc price ..')
fig.show()
while i<600:
time.sleep(0.25)
i=i+60
part_df=df.iloc[i:i+60]
fig = px.line(part_df, x = 'date_x', y = 'price_y', title='btc price ..')
fig.show()
so something like:
fig.update(px.line(part_df, x = 'date_x', y = 'price_y', title='btc price ..'))
instead of
fig = px.line(part_df, x = 'date_x', y = 'price_y', title='btc price ..')
fig.show()
I've put together an example plotly-dash app that performs a live update for data over a predefined period of time. Some of the important features are:
the dcc.Interval object automatically triggers the callback function for updating the figure every interval of time (e.g. update the figure every 1 second). there's also an n_interval counter which will be useful for helping us keep track of the indices in the df we are iterating through (documentation here)
the extendData property of dcc.Graph allows you to return a dictionary from your callback (and saves you the trouble of having to directly modify the data inside the figure object). (documentation here)
import numpy as np
import pandas as pd
import plotly.express as px
import plotly.graph_objects as go
import dash
from dash import dcc, html
from dash.dependencies import Input, Output
# df = pd.read_csv('btc.csv')
## create some random walk data
np.random.seed(42)
change = np.random.choice([-1,0,1], 600-1)
btc = [20000]
for y_change in change:
btc_new = btc[-1] + 1000*y_change
btc.append(btc_new)
df = pd.DataFrame({
'date': pd.date_range(start='2022-01-01', periods=600),
'btc': btc
})
part_df=df.iloc[0:60]
fig = px.line(part_df, x = 'date', y = 'btc', title='btc price')
app = dash.Dash(__name__)
app.layout = html.Div(
html.Div([
dcc.Graph(id='live-update-graph', figure=fig),
dcc.Interval(
id='interval-component',
interval=1000, # 0.25*1000, # in milliseconds
n_intervals=1,
max_intervals=len(df) // 60 - 1
)
])
)
#app.callback(Output('live-update-graph', 'extendData'),
[Input('interval-component', 'n_intervals'),
Input('live-update-graph', 'figure')])
def extend_trace(n, fig):
if 60*n > len(df):
return {}
else:
part_df = df.iloc[60*n:60*(n+1)]
return (dict(
x=[part_df['date'].tolist()],
y=[part_df['btc'].tolist()],
))
app.run(debug=True)
Update: if you don't need to the data to extend, and only want to show the new incoming data, then you can use the figure property of dcc.Graph instead of extendData. Then your callback would look like the following:
#app.callback(Output('live-update-graph', 'figure'),
[Input('interval-component', 'n_intervals'),
Input('live-update-graph', 'figure')])
def extend_trace(n, fig):
if 60*n > len(df):
return fig
else:
part_df = df.iloc[60*n:60*(n+1)]
return (dict(
data=[dict(
x=part_df['date'].tolist(),
y=part_df['btc'].tolist(),
)]
))
I'm trying to display live location data on a mapbox scatter plot. In order to mimic new data received from the server the callback moves all points every 3 seconds:
import plotly.express as px
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
px.set_mapbox_access_token(open(".mapbox_token").read())
df = px.data.carshare()
app = Dash(__name__)
app.layout = html.Div([
dcc.Graph(id='map', animate=True),
dcc.Interval(
id='interval-component',
interval=3000,
)
])
#app.callback(Output('map', 'figure'), [Input('interval-component', 'n_intervals')])
def update_map(n):
df['centroid_lon'] += 0.01
fig = px.scatter_mapbox(df, lat="centroid_lat", lon="centroid_lon")
return fig
if __name__ == '__main__':
app.run_server(debug=True)
While the labels are correctly changing their location, the markers are stuck at their original positions.
result
I found a work around by having two callbacks.
my html looks like this dbc.Col > dcc.Graph(figure = fig)
#app.callback(
Output('graph-id','figure'),
Input('control-id', 'n_clicks')
)
def update_func(scatter_map_fig):
return go.Figure
The second callback returns a new graph component with the updated information of the figure
#app.callback(
Output('col-id','children'),
Input('graph-id', 'figure'),
State('date_range','start_date'),
State('date_range','end_date'),
)
def update_func_2(scatter_map_fig):
scatter_fig = go.Figure()
scatter_fig.update_layout(...)
... (put your figure logic here)
return dcc.Graph(figure=fig)
It is a bit janky but it works until there's a better solution. Hope it helps.
Hi I'm new to plotly dash. My objective is to draw a graph when excel sheet is updated.
The lines are updated when I add a new row to sheet but axises are not animated or updated. But when I refresh the browser it graph was updated and axis also updated. Here is my code. Any help would be highly appreciated
import dash
from datetime import datetime as dt
from dash.dependencies import Output, Input
from dash import dcc
from dash import html
from plotly.subplots import make_subplots
import plotly.graph_objs as go
import pandas as pd
def check_acc_data():
df = pd.read_excel("test.xlsx")
last_10_df = df.tail(10)
# print(last_10_df)
time_ = last_10_df['time'].values.tolist()
# time_2=["14:00","14:30","15:00","15:30","16:00","16:30","17:00","17:30","18:00","18:30"]
temp = last_10_df['temperature'].values.tolist()
humidity = last_10_df['Humidity'].values.tolist()
test_str = [date_obj.strftime('%H:%M') for date_obj in time_]
return test_str,temp,humidity
app = dash.Dash(__name__)
#fig = px.line(x=test_str, y=[temp, humidity])
app.layout = html.Div([
html.H4('Dashboard'),
dcc.Interval('graph-update', interval = 2000, n_intervals = 0),
html.Div(children='''
Temperature Humidity and Time variation.
'''),
dcc.Graph(
id='example-graph',
figure={},
animate=True
)
])
#app.callback(
dash.dependencies.Output('example-graph','figure'),
[dash.dependencies.Input('graph-update', 'n_intervals')])
def update_figure(n):
time1,temp1,humid1=check_acc_data()
fig = make_subplots(specs=[[{"secondary_y": True}]])
fig.add_trace(
go.Scatter(x=time1, y=temp1, mode="lines+markers", name='Temperature'),
secondary_y=False
)
fig.add_trace(
go.Scatter(x=time1, y=humid1, mode="lines+markers", name='Humidity'),
secondary_y=True,
)
fig.update_layout(yaxis=dict(range=[-2.5,50]),
yaxis1=dict(range=[-2.5,100]),
)
return fig
if __name__ == '__main__':
app.run_server(debug=True, port=10451)
import dash
from dash import Dash, html, dcc, Output, Input, callback
import plotly.graph_objects as go
import plotly.express as px
df1 = pd.read_csv(filepath+filename, index_col="Date")
df1.index = pd.to_datetime(df1.index)
df1["Measure1_SMA"] = df1["Measure1"].rolling(20).mean()
df1["Measure2_SMA"] = df1["Measure2"].rolling(20).mean()
app = Dash(__name__)
my_dropdown = dcc.Dropdown(options = ['Measure1', 'Measure2'],
value = df1.columns[:2],
multi = False,
style = {'width':'50%'})
my_graph = dcc.Graph(figure={})
app.layout = html.Div([
html.H1('Metrics (Values)', style = {'textAlign':'center'}),
html.Label("Metrics: "),
my_dropdown,
my_graph
])
#callback(
Output(component_id=my_graph, component_property='figure'),
Input(component_id=my_dropdown, component_property='value')
)
def update_graph(dropdown_value):
plot_figure = px.bar(data_frame=df1, y=dropdown_value, x=df1.index)
#plot_figure.add_line()
print(dropdown_value)
return plot_figure
if __name__ == "__main__":
app.run_server(debug=True)
I want to create a single plot on the plotly dashboard with an option to toggle between Measure1 and Measure2. Selecting the dropdown_value will create a bar graph of Measure1 on y-axis and Date on x-axis. I also want to plot a line graph on the same plot which will be the rolling average of previous 20 days for the value selected from the dropdown.
I tried adding a add_line() method but not sure how to use it.
Creates a data frame from which the value columns and SMA columns are extracted, using the values obtained from the drop-down selections. Draw a bar graph in the created data frame and add the SMA in scatter plot line mode. drawing two graphs, I think I need to make a graph with two axes. since I could not add graphs to px.line, I reused the data in px.line to create the first I have used the data from px.line as the first graph. The sample data is stock price data.
import dash
from dash import Dash, html, dcc, Output, Input, callback
import plotly.graph_objects as go
import plotly.express as px
from plotly.subplots import make_subplots
import yfinance as yf
df1 = yf.download("AAPL", start="2021-01-01", end="2021-03-01")
df1["Close_SMA"] = df1["Close"].rolling(20).mean()
df1["High_SMA"] = df1["High"].rolling(20).mean()
df1 = df1[['High','Close','Close_SMA','High_SMA']]
app = Dash(__name__)
my_dropdown = dcc.Dropdown(options = ['Close', 'High'],
value = 'Close',
multi = False,
style = {'width':'50%'})
my_graph = dcc.Graph(figure={})
app.layout = html.Div([
html.H1('Metrics (Values)', style = {'textAlign':'center'}),
html.Label("Metrics: "),
my_dropdown,
my_graph
])
#callback(
Output(component_id=my_graph, component_property='figure'),
Input(component_id=my_dropdown, component_property='value')
)
def update_graph(dropdown_value):
sma = '{}_SMA'.format(dropdown_value)
dff = df1[[dropdown_value,sma]]
dff = dff.dropna()
plot_figure = make_subplots(specs=[[{"secondary_y": True}]])
fig = px.bar(data_frame=dff, y=dropdown_value, x=dff.index)
plot_figure.add_trace(fig.data[0], secondary_y=False)
plot_figure.add_trace(go.Scatter(x=dff.index, y=dff[sma], name=sma, mode='lines'), secondary_y=True)
plot_figure.update_layout(yaxis_title='Close')
return plot_figure
if __name__ == "__main__":
app.run_server(debug=True)
I am trying to use and adjust the rangeslider property in the update_layout of a Python Dash core component Graph. I like to rebase data to a relative performance chart whenever the rangeslider is changed such that the performance is always measured to the first element in the selected range. I was able to accomplish this by using a RangeSlider as an input for the Graph component. However, I was wondering if this is also possible only using the rangeslider property in the Graph component directly without the callback.
I am looking for something similar to this solution in R: https://mgei.github.io/post/rangeslider-plotly/.
# Libraries
import pandas as pd
import numpy as np
import datetime
from dash import Dash, html
import dash_core_components as dcc
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
import plotly.express as px
# Data
T = 100
steps = 10
base = datetime.datetime.today()
date_list = reversed([base - datetime.timedelta(days=x) for x in range(T)])
test_data = pd.DataFrame(np.random.randn(T)/100, index=date_list, columns=['Col1'])
test_data.iloc[0,:] = 0
# App
app = Dash(__name__)
app.layout = html.Div([html.H3('RangeSlider'),
dcc.RangeSlider(0, T, steps, count=1,
marks={i:test_data.index[i].strftime('%d.%m.%y') for i in range(0,T,steps)},
id='range_slider'),
html.Br(),
html.H3('Plot'),
dcc.Graph(figure={'data':[]}, id='plot_data'),
],
style={'width': '50%', 'display': 'inline-block', 'padding-left':'25%', 'padding-right':'25%'}
)
# Callbacks
#app.callback(Output('plot_data', 'figure'),
Input('range_slider', 'value'))
def plot_data(value):
if value is None:
raise PreventUpdate
else:
tmp_data = (1+test_data.iloc[value[0]:value[1],:]).cumprod() * 100
tmp_data.iloc[0,:] = 100
tmp_data = tmp_data.sort_index()
fig = px.line(tmp_data, y=['Col1'])
fig.update_layout(xaxis=dict(rangeslider=dict(visible=True), type='date'))
fig.update_layout(showlegend=True)
return fig
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)
The reason why it did not work is probably due to the fact that the time series data of the test data does not match the time series data of the slider.
My fixes included changing the date in datetime.date.today() to a date, changing the date format of the slider to a hyphenated format (unrelated to the failure), expanding the slider to 75% of the browser width because it was too short, and adjusting the margins.
# Libraries
import pandas as pd
import numpy as np
import datetime
from dash import Dash, html, dcc
from dash.dependencies import Input, Output
from dash.exceptions import PreventUpdate
from jupyter_dash import JupyterDash
import plotly.express as px
# Data
T = 100
steps = 10
base = datetime.date.today()
date_list = reversed([base - datetime.timedelta(days=x) for x in range(T)])
test_data = pd.DataFrame(np.random.randn(T)/100, index=date_list, columns=['Col1'])
test_data.iloc[0,:] = 0
#fig = px.line(test_data, y=['Col1'])
# App
#app = Dash(__name__)
app = JupyterDash(__name__)
app.layout = html.Div([html.H3('RangeSlider'),
dcc.RangeSlider(0, T, steps, count=1,
marks={i:test_data.index[i].strftime('%d-%m-%y') for i in range(0,T,steps)},
id='range_slider'),
html.Br(),
html.H3('Plot'),
dcc.Graph(figure={'data':[]}, id='plot_data'),
],
style={'width': '75%', 'display': 'inline-block', 'padding-left':'10%', 'padding-right':'15%'}
)
# Callbacks
#app.callback(Output('plot_data', 'figure'),
Input('range_slider', 'value'))
def plot_data(value):
if value is None:
raise PreventUpdate
else:
tmp_data = (1+test_data.iloc[value[0]:value[1],:]).cumprod() * 100
tmp_data.iloc[0,:] = 100
tmp_data = tmp_data.sort_index()
print(tmp_data)
fig = px.line(tmp_data, y=['Col1'])
fig.update_layout(xaxis=dict(rangeslider=dict(visible=True), type='date'))
fig.update_layout(showlegend=True)
return fig
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False, mode='inline')