I am trying to add points to a predefined figure through a dropdown in Dash. If I select one value I want to add a point in a pair of coordinates, and if I select a different value I want to update the graph in a different pair of coordinates. First of all the function to graph the figure is the following:
import plotly.graph_objects as go
import plotly as py
py.offline.init_notebook_mode(connected = True)
def graficar_rectangulos(fig):
fig.add_trace(go.Scatter(
x=[1.5],
y=[0.75],
mode="text",
))
# Set axes properties
fig.update_xaxes(range=[0, 7], showgrid=False)
fig.update_yaxes(range=[0, 3.5])
# Add shapes
fig.add_shape(
# unfilled Rectangle
type="rect",
x0=1,
y0=1,
x1=2,
y1=3,
line=dict(
color="RoyalBlue",
),
)
fig.update_shapes(dict(xref='x', yref='y'))
return True
After that is where I have the problem and specifically updating the graph. I am not sure which are the parameters that should be returned by the function update graph to update the figure with the scatter plots (I am using Jupyter notebook):
from jupyter_plotly_dash import JupyterDash
import plotly.graph_objects as go
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = JupyterDash('SimpleExample')
fig = go.Figure()
graficar_rectangulos(fig)
app.layout = html.Div([
dcc.Dropdown(
id='dropdown',
options=[
{'label': 'A', 'value': 'A'},
{'label': 'B', 'value': 'B'},
{'label': 'C', 'value': 'C'}
],
value='NYC'
),
dcc.Graph(id='graph-court', figure = fig)
]
)
#app.callback(
Output('graph-court', 'figure'),
[Input('dropdown', 'value')])
def update_figure(selected_value):
if selected_value=='A':
x,y=3,3
else:
x,y=2,2
return add_trace(go.Scatter(x=x,y=y,marker = dict(size=[15], color=['green']), mode='markers'))
app
How does the function update_figure should work?
import dash
import plotly as py
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objects as go
from jupyter_plotly_dash import JupyterDash
py.offline.init_notebook_mode(connected = True)
app = JupyterDash('SimpleExample')
app.layout = html.Div([
dcc.Dropdown(id='dropdown', options=[
{'label': 'A', 'value': 'A'},
{'label': 'B', 'value': 'B'}],
value = 'A'),
dcc.Graph(id='graph-court')
])
def create_figure():
layout = dict(xaxis=dict(range=[0, 7], showgrid=False), yaxis=dict(range=[0, 3.5]), showlegend=False,
shapes=[dict(type='rect', x0=1, y0=1, x1=2, y1=3, line=dict(color='RoyalBlue'))])
data = go.Scatter(x=[1.5], y=[0.75], text='Some text', mode='text')
fig = go.Figure(data=data, layout=layout)
return fig
#app.callback(Output('graph-court', 'figure'),
[Input('dropdown', 'value')])
def update_figure(selected_value):
if selected_value == 'A':
x, y = 3, 3
else:
x, y = 2, 2
fig = create_figure()
fig.add_trace(go.Scatter(x=[x], y=[y], marker=dict(size=15, color='green'), mode='markers'))
return fig
app
Related
I'm developing my first Dash Plotly metrics dashboard. That stated, I was able to get most of the dashboard dark themed except the area around my plotly graph object. See below:
How can I get the area around my graph the same dark theme as the rest of the dashboard?
Below is the code I used to materialize the dashboard.
import dash
from dash import Dash, dcc, dash_table
import dash_bootstrap_components as dbc
import dash_design_kit as ddk
from dash import html
import plotly.express as px
import plotly.graph_objects as go
import plotly.io as pio
from google.cloud import bigquery
import pandas as pd
import numpy as np
import os
from google.oauth2 import service_account
df = query_job.to_dataframe()
df['dmand_month'] = pd.to_datetime(df['dmand_yr_mo']).dt.month
df.loc[:, 'price'] = df.loc[:, 'sales_dollars']/df.loc[:, 'ord_qty']
df.loc[:, 'predicted_sales'] = np.round(df.loc[:, 'predictions']*df.loc[:, 'price'])
df.loc[:, 'diff'] = np.round(np.abs(np.round(df.loc[:, 'predicted_sales'] - df.loc[:, 'sales_dollars'],2)))
#BUSINESS LINE
bl = df.groupby(['location_type', 'GLBL_BUS_LN_DESC']).agg({'ord_qty':'sum',
'predictions':'sum',
'sales_dollars':'sum',
'predicted_sales':'sum',
'diff':'sum'}).reset_index()
bl.replace([np.inf, -np.inf], np.nan, inplace=True)
bl['sales_dollars'] = np.round(bl['sales_dollars'])
bl.dropna(inplace = True)
bl.loc[:, 'MAPE'] = np.round(np.round(bl.loc[:, 'diff']/ bl.loc[:, 'sales_dollars'], 4) * 100,2)
bl.loc[:, 'BIAS'] = np.round(np.round((bl.loc[:,'predicted_sales']- bl.loc[:, 'sales_dollars'])/ bl.loc[:, 'sales_dollars'], 4) * 100,2)
fig1 = go.Figure(data=[
go.Bar( name='MAPE', x=bl['GLBL_BUS_LN_DESC'], y=bl['MAPE']),
go.Bar(name='BIAS', x=bl['GLBL_BUS_LN_DESC'], y=bl['BIAS'])
])
fig1.update_layout(barmode='group', plot_bgcolor='rgb(0,0,0)')
colors = {
'background': '#000000',
'text': '#111111'
}
# initialize app
app = dash.Dash(__name__, external_stylesheets=[dbc.themes.DARKLY])
# set app layout
app.layout = html.Div(children=[
html.H1('Demand Forecasting Model Overview',style={
'textAlign': 'center'}),
html.Div(children='''
Dash: A web application to help assess predictors for furture demand forecasting models.
''', style={
'textAlign': 'center'}),
html.Br(),
dcc.Dropdown(options = [{'label':'home_delivery', 'value':'home_delivery'},
{'label': 'shop_sales', 'value':'shop+sales'}],
id='location-dropdown'),
dcc.Graph(id='Business Line MAPE', figure=fig1),
html.H2(children='Demand Forecast by Business Line', style={
'textAlign': 'center'}),
dash_table.DataTable(
data=bl.to_dict('records'),
columns=[{'id': c, 'name': c} for c in bl.columns],
style_header={
'backgroundColor': 'rgb(30, 30, 30)',
'color': 'white'
},
style_data={
'backgroundColor': 'rgb(50, 50, 50)',
'color': 'white'
},
)
# dcc.Graph(id='Business Line BIAS', figure=fig2),
# dcc.Graph(id='Month', figure=fig_month)
])
if __name__ == '__main__':
app.run_server(debug=True)
You should add paper_bgcolor in your update_layout as below:
fig.update_layout(barmode='group', plot_bgcolor='rgb(0,0,0)',paper_bgcolor='rgb(0,0,0)')
I would like to add a range slider along with my dropdown, and make the range slider the 'Wallclock' datetime along with an interaction that allows the range slider to chose the datetime for that capsules based on the dropdown value. I managed to find several ways that other people have done this but none seems to work for my situation especially the callback and the update of the graph. Thank you!
Data looks like this.
Dash looks like this.
Code looks like this.
import pandas as pd
import plotly.express as px # (version 4.7.0)
import plotly.graph_objects as go
import numpy as np
import openpyxl
import dash # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
app = dash.Dash(__name__)
server = app.server
df = pd.read_excel("tcd vs rh 2.xlsx")
print(df)
capsuleID = df['Capsule_ID'].unique()
print(capsuleID)
capsuleID_names = sorted(list(capsuleID))
print(capsuleID_names)
capsuleID_names_1 = [{'label': k, 'value': k} for k in sorted(capsuleID)]
capsuleID_names_2 = [{'label': '(Select All)', 'value': 'All'}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2
app.layout = html.Div([
html.H1("Relative Humidity vs TCD", style={'text-align': 'center'}),
dcc.Dropdown(id="capsule_select",
options=capsuleID_names_all,
optionHeight=25,
multi=True,
searchable=True,
placeholder='Please select...',
clearable=True,
value=['All'],
style={'width': "100%"}
),
dcc.RangeSlider(id='slider',
min=df['Wallclock'].min(),
max=df['Wallclock'].max(),
value=[df.iloc[-101]['Wallclock'].timestamp(), df.iloc[-1]['Wallclock'].timestamp()]
),
html.Div([
dcc.Graph(id="the_graph"),
]),
])
# -----------------------------------------------------------
#app.callback(
Output('the_graph', 'figure'),
Output('capsule_select', 'value'),
Input('capsule_select', 'value'),
Input('slider', 'value'),
)
def update_graph(capsule_chosen):
lBound = pd.to_datetime(value[0], unit='s')
uBound = pd.to_datetime(value[1], unit='s')
filteredData = df.loc[(df['date'] >= lBound) & (df['date'] <= uBound)]
dropdown_values = capsule_chosen
if "All" in capsule_chosen:
dropdown_values = capsuleID_names
dff = df
else:
dff = df[df['Capsule_ID'].isin(capsule_chosen)] # filter all rows where capsule ID is the capsule ID selected
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
hover_name="Wallclock",
)
scatterplot.update_traces(textposition='top center')
return scatterplot, dropdown_values
# ------------------------------------------------------------------------------
if __name__ == '__main__':
app.run_server(debug=True)
obviously I don't have access to your Excel spreadsheet so generated a data frame with same shape
taken approach of using a second figure with a rangeslider for slider capability
updated callback to use this figure as input for date range
used jupyter dash inline, this can be changed back to your setup (commented lines)
generate some sample data
import pandas as pd
import numpy as np
df = pd.DataFrame(
{
"Wallclock": pd.date_range(
"22-dec-2020 00:01:36", freq="5min", periods=2000
),
"tcd": np.linspace(3434, 3505, 2000) *np.random.uniform(.9,1.1, 2000),
"humidity": np.linspace(63, 96, 2000),
}
).pipe(lambda d: d.assign(Capsule_ID=(d.index // (len(d)//16))+2100015))
slider is a figure with a rangeslider
import pandas as pd
import plotly.express as px # (version 4.7.0)
import plotly.graph_objects as go
import numpy as np
import openpyxl
import dash # (version 1.12.0) pip install dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
from dash.exceptions import PreventUpdate
from jupyter_dash import JupyterDash
# app = dash.Dash(__name__)
# server = app.server
app = JupyterDash(__name__)
# df = pd.read_excel("tcd vs rh 2.xlsx")
# print(df)
capsuleID = df["Capsule_ID"].unique()
# print(capsuleID)
capsuleID_names = sorted(list(capsuleID))
# print(capsuleID_names)
capsuleID_names_1 = [{"label": k, "value": k} for k in sorted(capsuleID)]
capsuleID_names_2 = [{"label": "(Select All)", "value": "All"}]
capsuleID_names_all = capsuleID_names_1 + capsuleID_names_2
def slider_fig(df):
return px.scatter(
df.groupby("Wallclock", as_index=False).size(), x="Wallclock", y="size"
).update_layout(
xaxis={"rangeslider": {"visible": True}, "title":None},
height=125,
yaxis={"tickmode": "array", "tickvals": [], "title": None},
margin={"l": 0, "r": 0, "t": 0, "b": 0},
)
app.layout = html.Div(
[
html.H1("Relative Humidity vs TCD", style={"text-align": "center"}),
dcc.Dropdown(
id="capsule_select",
options=capsuleID_names_all,
optionHeight=25,
multi=True,
searchable=True,
placeholder="Please select...",
clearable=True,
value=["All"],
style={"width": "100%"},
),
dcc.Graph(
id="slider",
figure=slider_fig(df),
),
html.Div(
[
dcc.Graph(id="the_graph"),
]
),
]
)
# -----------------------------------------------------------
#app.callback(
Output("the_graph", "figure"),
Output("capsule_select", "value"),
Output("slider", "figure"),
Input("capsule_select", "value"),
Input('slider', 'relayoutData'),
State("slider", "figure")
)
def update_graph(capsule_chosen, slider, sfig):
dropdown_values = capsule_chosen
if "All" in capsule_chosen:
dropdown_values = capsuleID_names
dff = df
else:
dff = df[
df["Capsule_ID"].isin(capsule_chosen)
] # filter all rows where capsule ID is the capsule ID selected
if slider and "xaxis.range" in slider.keys():
dff = dff.loc[dff["Wallclock"].between(*slider["xaxis.range"])]
else:
# update slider based on selected capsules
sfig = slider_fig(dff)
scatterplot = px.scatter(
data_frame=dff,
x="tcd",
y="humidity",
hover_name="Wallclock",
)
scatterplot.update_traces(textposition="top center")
return scatterplot, dropdown_values, sfig
# ------------------------------------------------------------------------------
if __name__ == "__main__":
# app.run_server(debug=True)
app.run_server(mode="inline")
New to Plotly Dash.
I’m working through the tutorial on my simple example. Learning how to update a graph when new data is added to the data frame (two data frames in this case) and how to connect this with the dropdown that I have on my dashboard.
I want my graphs to get updated with new data on each page load or page refresh (as I will have only a few updates per day.)
This is the code I'm working on:
import pandas as pd
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
data = [['Blue', 20], ['Red ', 12], ['Green', 33]]
df = pd.DataFrame(data, columns=['Color', 'Number'])
data1 = [['A', 10, 88], ['B ', 50, 45], ['C', 25, 120]]
df1 = pd.DataFrame(data1, columns=['Letter', 'Column1', 'Column2'])
fig = px.bar(df, x=df['Color'], y=df['Number'])
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
html.Div([
html.Label(['Choose a graph:'], style={'font-weight': 'bold'}),
dcc.Dropdown(
id='dropdown',
options=[
{'label': 'Colors', 'value': 'graph1'},
{'label': 'Letters', 'value': 'graph2'},
],
value='graph1',
style={"width": "60%"}),
html.Div(dcc.Graph(id='graph')),
]),
])
#app.callback(
Output('graph', 'figure'),
[Input(component_id='dropdown', component_property='value')]
)
def select_graph(value):
if value == 'graph1':
return fig
else:
return fig1
if __name__ == '__main__':
app.run_server(debug=True)
Any help would be greatly appreciated. Thanks in advance.
As documented in the section on live update, you should be able to achieve the desired behaviour by defining a function that creates the layout,
def layout():
return html.Div(...)
and assigning this function as the app layout,
app.layout = layout # note no (), you must assign the function itself, not the layout
I am building the app in which I want to plot two separate graphs from two data frames. I would like to use the dropdown to display only one graph per page based on each data frame (df,df1).
I followed the Plotly reference and couldn't reproduce it on my example.
This is my code so far that works (it displays both graphs on the same page, without dropdown):
import pandas as pd
import dash
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
app = dash.Dash(__name__)
data = [['Blue',20],['Red ',12],['Green',33]]
df = pd.DataFrame(data,columns=['Color','Number'])
data1 = [['A',10,88],['B ',50,45],['C',25,120]]
df1 = pd.DataFrame(data1,columns=['Letter','Column1','Column2'])
fig = px.bar(df, x=df['Color'], y=df['Number'])
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
dcc.Graph(
id='example-graph',
figure=fig
),
html.Div(children='Letter', style={'text-align': 'center'}),
dcc.Graph(
id='example-graph1',
figure=fig1
)
])
if __name__ == '__main__':
app.run_server(debug=True)
This is what I would like to get:
What would be the best approach to do this?
Thanks in advance.
I don't have much experience with Dash, but here's a great answer and your code throughout. The point is to introduce a dropdown so that the initial value is displayed as fig. Depending on the return value of the callback function, I use the if function to switch the graph.
import pandas as pd
import plotly.express as px
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
data = [['Blue',20],['Red ',12],['Green',33]]
df = pd.DataFrame(data,columns=['Color','Number'])
data1 = [['A',10,88],['B ',50,45],['C',25,120]]
df1 = pd.DataFrame(data1,columns=['Letter','Column1','Column2'])
app.layout = html.Div(children=[
html.H1(children='Colors and Letters', style={'text-align': 'center'}),
html.Div(children='Color', style={'text-align': 'center'}),
html.Div([
html.Label(['Choose a graph:'],style={'font-weight': 'bold'}),
dcc.Dropdown(
id='dropdown',
options=[
{'label': 'graph1', 'value': 'graph1'},
{'label': 'graph2', 'value': 'graph2'},
],
value='graph1',
style={"width": "60%"}),
html.Div(dcc.Graph(id='graph')),
]),
])
#app.callback(
Output('graph', 'figure'),
[Input(component_id='dropdown', component_property='value')]
)
def select_graph(value):
if value == 'graph1':
fig = px.bar(df, x=df['Color'], y=df['Number'])
return fig
else:
fig1 = px.line(x=df1['Letter'], y=df1['Column1'], color=px.Constant('Column1'),
labels=dict(x='Letter', y='Column1', color='Letter'))
fig1.add_bar(x=df1['Letter'], y=df1['Column2'], name='Letter')
return fig1
if __name__ == '__main__':
app.run_server(debug=True)
I want to make an interactive map with dash. So I added a Scattermapbox to visualize some data.
To select which data is shown, I added a slider. and used a callback to redraw the map.
import dash
import dash_html_components as html
import dash_core_components as dcc
from dash.dependencies import Input, Output
import pandas as pd
import plotly.graph_objects as go
df = pd.DataFrame({'place_no': [1, 1, 1, 2, 2, 2],
'lat': [50.941357, 50.941357, 50.941357, 50.932171, 50.932171, 50.932171],
'lon': [6.957768, 6.957768, 6.957768, 6.964412, 6.964412, 6.964412],
'year': [2017, 2018, 2019, 2017, 2018, 2019],
'value': [20, 40, 60, 80, 60, 40]})
def get_map(df_map):
fig = go.Figure(go.Scattermapbox(
lat=df_map['lat'],
lon=df_map['lon'],
mode='markers',
marker=go.scattermapbox.Marker(
size=df_map['value']
),
))
fig.update_layout(
mapbox_style="open-street-map",
mapbox={'center': go.layout.mapbox.Center(lat=50.936600, lon=6.961497), 'zoom': 11}
)
return fig
app = dash.Dash()
app.layout = html.Div([
dcc.Graph(id='map',
figure=get_map(df[df['year'] == 2017])),
dcc.Slider(id='year-picker',
min=2017,
max=2019,
marks={2017: {'label': 2017}, 2018: {'label': 2018}, 2019: {'label': 2019}}
),
html.Div(id='shown-week', style={'textAlign': 'center'})
], )
#app.callback(
Output(component_id='map', component_property='figure'),
[Input(component_id='year-picker', component_property='value')]
)
def update_map(selected_year):
filtered_df = df[df['year'] == selected_year]
fig = get_map(filtered_df)
return fig
if __name__ == '__main__':
app.run_server()
This worked out fine for now. And I get exact the result I expect.
But if move or zoom the map, and then select new data with the slider, the center and the zoom goes back to the initial values, which is very inconvenient.
Is there any way to get the center and zoom of the current view, so I can add it in the update-layout method?
Or is there any other way to save the current view?
Have you tried the uirevision property? If you set this property to a constant value when you update the figure, i.e.
fig['layout']['uirevision'] = 'some-constant'
the zoom etc. should stay the same. For details, see the documentation.