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
Related
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.
I'm trying to hide time gaps in the stock market. My problem is that I don't know how to use rangebreaks correctly in dash. https://plotly.com/python/reference/#layout-xaxis-rangebreaks
https://plotly.com/python/time-series/
app = dash.Dash()
app.layout = html.Div([
dcc.Graph(id='graph'),
dcc.Dropdown(id='chooser',options=companies_options,value='CDPROJEKT')
])
#app.callback(Output('graph', 'figure'),
[Input('chooser', 'value')])
def update_figure(selected_company):
df_by_company = df_full[df_full['Nazwa'] == selected_company]
df_by_company= df_by_company[(df_by_company['date'].dt.hour<17) & (df_by_company['date'].dt.hour>8)]
traces = []
print(df_by_company['date'].unique())
traces.append(go.Scatter(
x=df_by_company['date'],
y=df_by_company['Kurs'],
text=df_by_company['Nazwa'],
mode='markers',
opacity=0.7,
#marker={'size': 15},
name=selected_company
))
return {
'data': traces,
'layout': go.Layout(
xaxis= dict(title= 'Time',rangebreaks=[dict(bounds=[17, 8])]),
yaxis={'title': 'Price' },
hovermode='closest'
)
}
if __name__ == '__main__':
app.run_server()
This is my output graph.
https://prnt.sc/rttbkk
Edit:
When I simplified the code I found a solution for my problem.
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
import pandas as pd
df= pd.read_csv('cdr.csv')
app = dash.Dash()
app.layout = html.Div([dcc.Graph(id='scatterplot',
figure = {'data':[
go.Scatter(
x=df['date'],
y=df['price'],
mode='markers')],
'layout':go.Layout(title='My Scatterplot',
xaxis= dict(title= 'Time', rangebreaks=[{ 'pattern': 'hour', 'bounds': [17.5, 8.5] } ]) )}
)])
if __name__ == '__main__':
app.run_server()
My csv file looks like that:
date,number,price
2020-04-03 17:04:15,8838,297.0
https://prnt.sc/ruyob3
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
Is it possible to have a dash app within a Jupyter Notebook, rather than served up and viewed in a browser?
My intention is to link graphs within a Jupyter notebook so that hovering over one graph generates the input required for another graph.
(Disclaimer, I help maintain Dash)
See https://github.com/plotly/jupyterlab-dash. This is a JupyterLab extension that embeds Dash within Jupyter.
Also see alternative solutions in the Dash Community Forum like the Can I run dash app in jupyter topic.
There's already a great answer to this question, but this contribution will focus directly on:
1. How to use Dash within Jupyterlab, and
2. how to select graphing input by hovering over another graph
Following these steps will unleash Plotly Dash directly in JupyterLab:
1. Install the latest Plotly version
2. Installl JupyterLab Dash with conda install -c plotly jupyterlab-dash
3. Using the snippet provided a bit further down launch a Dash app that contains an animation built on a pandas dataframe that expands every second.
Screenshot of the Dash in JupyterLab (code in snippet below)
This image shows Dash literally fired up inside JupyterLab. The four highlighted sections are:
1 - Cell. A cell in a .ipynb that you're already probably very familiar with
2 - Dash. A "live" dash app that expands all three traces with a random number and shows the updated figure every second.
3 - Console. An console where you can inspect available elements in your script using, for example, fig.show
4 - mode. This shows where some true magic resides:
app.run_server(mode='jupyterlab', port = 8090, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)
You can choose to fire up the dash app in:
Jupyterlab, like in the screenshot with mode='jupyterlab',
or in a cell, using mode='inline':
or in your default browser using mode='external'
Code 1:
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
# code and plot setup
# settings
pd.options.plotting.backend = "plotly"
# sample dataframe of a wide format
np.random.seed(4); cols = list('abc')
X = np.random.randn(50,len(cols))
df=pd.DataFrame(X, columns=cols)
df.iloc[0]=0;
# plotly figure
fig = df.plot(template = 'plotly_dark')
app = JupyterDash(__name__)
app.layout = html.Div([
html.H1("Random datastream"),
dcc.Interval(
id='interval-component',
interval=1*1000, # in milliseconds
n_intervals=0
),
dcc.Graph(id='graph'),
])
# Define callback to update graph
#app.callback(
Output('graph', 'figure'),
[Input('interval-component', "n_intervals")]
)
def streamFig(value):
global df
Y = np.random.randn(1,len(cols))
df2 = pd.DataFrame(Y, columns = cols)
df = df.append(df2, ignore_index=True)#.reset_index()
df.tail()
df3=df.copy()
df3 = df3.cumsum()
fig = df3.plot(template = 'plotly_dark')
#fig.show()
return(fig)
app.run_server(mode='jupyterlab', port = 8090, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)
But the good news does not end there, regarding:
My intention is to link graphs within a Jupyter notebook so that
hovering over one graph generates the input required for another
graph.
There's a perfect example on dash.plotly.com that will do exactly that for you under the paragraph Update Graphs on Hover:
I've made the few necessary changes in the original setup to make it possible to run it in JupyterLab.
Code snippet 2 - Select graph source by hovering:
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import dash.dependencies
# code and plot setup
# settings
pd.options.plotting.backend = "plotly"
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')
available_indicators = df['Indicator Name'].unique()
app.layout = html.Div([
html.Div([
html.Div([
dcc.Dropdown(
id='crossfilter-xaxis-column',
options=[{'label': i, 'value': i} for i in available_indicators],
value='Fertility rate, total (births per woman)'
),
dcc.RadioItems(
id='crossfilter-xaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
],
style={'width': '49%', 'display': 'inline-block'}),
html.Div([
dcc.Dropdown(
id='crossfilter-yaxis-column',
options=[{'label': i, 'value': i} for i in available_indicators],
value='Life expectancy at birth, total (years)'
),
dcc.RadioItems(
id='crossfilter-yaxis-type',
options=[{'label': i, 'value': i} for i in ['Linear', 'Log']],
value='Linear',
labelStyle={'display': 'inline-block'}
)
], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
], style={
'borderBottom': 'thin lightgrey solid',
'backgroundColor': 'rgb(250, 250, 250)',
'padding': '10px 5px'
}),
html.Div([
dcc.Graph(
id='crossfilter-indicator-scatter',
hoverData={'points': [{'customdata': 'Japan'}]}
)
], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
html.Div([
dcc.Graph(id='x-time-series'),
dcc.Graph(id='y-time-series'),
], style={'display': 'inline-block', 'width': '49%'}),
html.Div(dcc.Slider(
id='crossfilter-year--slider',
min=df['Year'].min(),
max=df['Year'].max(),
value=df['Year'].max(),
marks={str(year): str(year) for year in df['Year'].unique()},
step=None
), style={'width': '49%', 'padding': '0px 20px 20px 20px'})
])
#app.callback(
dash.dependencies.Output('crossfilter-indicator-scatter', 'figure'),
[dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
dash.dependencies.Input('crossfilter-xaxis-type', 'value'),
dash.dependencies.Input('crossfilter-yaxis-type', 'value'),
dash.dependencies.Input('crossfilter-year--slider', 'value')])
def update_graph(xaxis_column_name, yaxis_column_name,
xaxis_type, yaxis_type,
year_value):
dff = df[df['Year'] == year_value]
fig = px.scatter(x=dff[dff['Indicator Name'] == xaxis_column_name]['Value'],
y=dff[dff['Indicator Name'] == yaxis_column_name]['Value'],
hover_name=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name']
)
fig.update_traces(customdata=dff[dff['Indicator Name'] == yaxis_column_name]['Country Name'])
fig.update_xaxes(title=xaxis_column_name, type='linear' if xaxis_type == 'Linear' else 'log')
fig.update_yaxes(title=yaxis_column_name, type='linear' if yaxis_type == 'Linear' else 'log')
fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')
return fig
def create_time_series(dff, axis_type, title):
fig = px.scatter(dff, x='Year', y='Value')
fig.update_traces(mode='lines+markers')
fig.update_xaxes(showgrid=False)
fig.update_yaxes(type='linear' if axis_type == 'Linear' else 'log')
fig.add_annotation(x=0, y=0.85, xanchor='left', yanchor='bottom',
xref='paper', yref='paper', showarrow=False, align='left',
bgcolor='rgba(255, 255, 255, 0.5)', text=title)
fig.update_layout(height=225, margin={'l': 20, 'b': 30, 'r': 10, 't': 10})
return fig
#app.callback(
dash.dependencies.Output('x-time-series', 'figure'),
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
dash.dependencies.Input('crossfilter-xaxis-column', 'value'),
dash.dependencies.Input('crossfilter-xaxis-type', 'value')])
def update_y_timeseries(hoverData, xaxis_column_name, axis_type):
country_name = hoverData['points'][0]['customdata']
dff = df[df['Country Name'] == country_name]
dff = dff[dff['Indicator Name'] == xaxis_column_name]
title = '<b>{}</b><br>{}'.format(country_name, xaxis_column_name)
return create_time_series(dff, axis_type, title)
#app.callback(
dash.dependencies.Output('y-time-series', 'figure'),
[dash.dependencies.Input('crossfilter-indicator-scatter', 'hoverData'),
dash.dependencies.Input('crossfilter-yaxis-column', 'value'),
dash.dependencies.Input('crossfilter-yaxis-type', 'value')])
def update_x_timeseries(hoverData, yaxis_column_name, axis_type):
dff = df[df['Country Name'] == hoverData['points'][0]['customdata']]
dff = dff[dff['Indicator Name'] == yaxis_column_name]
return create_time_series(dff, axis_type, yaxis_column_name)
app.run_server(mode='jupyterlab', port = 8090, dev_tools_ui=True, #debug=True,
dev_tools_hot_reload =True, threaded=True)
I am not sure dash apps can be displayed within a Jupyter notebook. But if what you're looking for is using sliders, combo boxes and other buttons, you may be interested in ipywidgets that come from Jupyter directly.
These may be used with plotly, as shown here.
EDIT
Eventually it seems that there are solutions to embed dash apps inside Jupyter by using an iframe and IPython.display.display_html().
See this function and this GitHub repo for details.
See https://medium.com/plotly/introducing-jupyterdash-811f1f57c02e
$ pip install jupyter-dash
from jupyter_dash import JupyterDash
app = JupyterDash(__name__)
<your code>
app.run_server(mode='inline')
Look for plotly offline.
Say you have a figure (e.g. fig = {'data': data, 'layout':layout} )
Then,
inside a jupyter notebook cell,
from plotly.offline import iplot, init_notebook_mode
init_notebook_mode()
# plot it
iplot(fig)
This will plot the plotly inside your jupyter. You dont even have to run the flask server.