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'm trying to create a dash app that updates a histogram depending on what cell is selected in the 'group' column.
I can get the table to display but having trouble with the histogram.
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
import pandas as pd
import plotly.express as px
import numpy as np
from dash import Dash, dash_table
import json
app = dash.Dash(__name__)
## create data
df_rand = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
df_rand = pd.melt(df_rand, value_vars=list('ABCD'))
df_rand_summary = df_rand.groupby('variable').describe()
df_rand_summary = df_rand_summary.droplevel(level=0, axis=1)
df_rand_summary.insert(0, 'group', df_rand_summary.index)
app.layout = html.Div(children=[
## add table
dash_table.DataTable(
data=df_rand_summary.to_dict('records'),
columns=[{'id': c, 'name': c, } for c in df_rand_summary]
),
# include histogram
html.Div([
dcc.Graph(
id='hist'
)
])
])
#app.callback(
Output('hist', 'figure'),
Input('table', 'active_cell'),
State('table', 'data'))
def update_hist(active_cell, df_rand):
# subset histogram with selected cell
# from 'group' column
cell = json.dumps(active_cell, indent=2)
row = active_cell['row']
col = active_cell['column_id']
value = df_rand[row][col]
fig = px.histogram(df_rand[df_rand['variable'] == value], x='value')
return fig
if __name__ == '__main__':
app.run_server(debug=True)
When you use df_rand as a parameter to update_hist, you overwrite the df_rand defined outside the update_hist. To solve this problem, define new parameter, instead as follows:
import dash
from dash import dcc
from dash import html
from dash.dependencies import Input, Output, State
import pandas as pd
import plotly.express as px
import numpy as np
from dash import Dash, dash_table
import json
app = dash.Dash(__name__)
## create data
df_rand = pd.DataFrame(np.random.randint(0, 100, size=(100, 4)), columns=list('ABCD'))
df_rand = pd.melt(df_rand, value_vars=list('ABCD'))
df_rand_summary = df_rand.groupby('variable').describe()
df_rand_summary = df_rand_summary.droplevel(level=0, axis=1)
df_rand_summary.insert(0, 'group', df_rand_summary.index)
app.layout = html.Div(children=[
## add table
dash_table.DataTable(id="table",
data=df_rand_summary.to_dict('records'),
columns=[{'id': c, 'name': c, } for c in df_rand_summary]
),
# include histogram
html.Div([
dcc.Graph(
id='hist'
)
])
])
#app.callback(
Output('hist', 'figure'),
Input('table', 'active_cell'),
State('table', 'data'),
prevent_initial_call=True)
def update_hist(active_cell, data_dict): #<------ here we define data_dict instead of df_rand
row = active_cell['row']
col = active_cell['column_id']
value = data_dict[row][col]
fig = px.histogram(df_rand.query("variable == #value"), x='value')
return fig
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)
Output:
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")
I am trying to add interactivity to a plotly 3d scatter plot which I am hosting using dash. My question contains two related parts:
(i) I would like to manually select points in my 3d scatter and change the selected points' colour to red. Selection should include click event and selection event.
(ii) I would like to be able to delete these points from the plot on pressing of a key e.g. 'delete' key.
Part (i) is similar to an example in the plotly guide https://plot.ly/python/click-events/, however, on_click is not available as a method for 3d scatter charts.
I have been attempting to use FigureWidget as that apparently provides methods to capture clicks and selections, but after 2 days I am struggling to make headway.
Example data (copy below to clipboard and run df = pd.read_clipboard(sep=','))
id,x_val,z_val,y_val
a,11.313449728149418,0.13039110880256777,0.5386387766748618
b,11.321463427315383,0.2360697833061771,1.32441455152796
c,10.127132005050942,0.23085014016641864,1.0961116175427044
d,11.639819269465233,0.0958798324712593,0.6506370305953094
e,8.892696370438149,0.08223988244819926,0.6440321391968353
f,6.711586646011124,0.3657515974938044,
g,7.095030650760687,,0.5723062047617504
h,6.4523124528415,,1.293852184258803
i,7.165105300812886,0.4151365420301895,-0.5920674079031845
j,7.480703395137295,0.14284429977557123,1.0600936940126982
k,5.570775744372319,,
l,4.358946555449826,,
I have example code below which I hope is almost there (but not quite). The idea is that 'handle_click' should capture click events. I should also handle 'selection' events, although I am not sure how to do this as 3d scatter plot does not provide a selection box or lassoo tool. With the callback, I'm not even sure how to initiate, as there is no clickData/selectionData event with 3d scatterplot that I can utilize (so [Input('subindustry-dropdown', 'value')]) is incorrect and note that subindustry-dropdown is not something that I have provided in the example, but I select my ids from a dropdown which returns a subindustry value in my development version. )
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
import plotly.graph_objs as go
import pandas as pd
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
app.layout = html.Div([html.Div(id = '3d-scatter'),
])
# Calculate and store data
#app.callback(Output('3d-scatter', 'children'),
[Input('subindustry-dropdown', 'value')])
def chart_3d():
f = go.FigureWidget(px.scatter_3d(df, x = 'x_val', y = 'y_val', z = 'z_val', hover_name = 'company_nm'))
f.layout.clickmode = 'event+select'
f.data[0].on_click(handle_click) # if click, then update point/df.
return dcc.Graph(id = '3d_scat', figure=f)
def handle_click(trace, points, selector):
c = list(f.data[0].marker.color)
s = list(f.data[0].marker.size)
for i in points.point_inds:
c[i] = '#bae2be'
s[i] = 20
with f.batch_update():
f.data[0].marker.color = c
f.data[0].marker.size = s
return f.data[0]
Here is a solution which allows:
selecting points by clicking individually
delete selected points by pressing html button
clear selection by pressing html button
according to this issue selecting multiple points in a 3d plot is currently not supported
(The use of FigureWidget does not seem to make a difference, so I removed it)
import dash
import plotly.express as px
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
import pandas as pd
import json
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets=external_stylesheets)
app.css.append_css({
"external_url": "https://codepen.io/chriddyp/pen/bWLwgP.css"
})
import pandas as pd
df = pd.DataFrame(columns=['x_val','z_val','y_val'])
df.loc['a'] = [11.313449728149418,0.13039110880256777,0.5386387766748618]
df.loc['b'] = [11.321463427315383,0.2360697833061771,1.32441455152796]
df.loc['c'] = [10.127132005050942,0.23085014016641864,1.0961116175427044]
df.loc['d'] = [11.639819269465233,0.0958798324712593,0.6506370305953094]
df.loc['e'] = [8.892696370438149,0.08223988244819926,0.6440321391968353]
df.loc['f'] = [6.711586646011124,0.3657515974938044,0]
df.loc['g'] = [7.095030650760687,0,0.5723062047617504]
df.loc['h'] = [6.4523124528415,0,1.293852184258803]
df.loc['i'] = [7.165105300812886,0.4151365420301895,-0.5920674079031845]
df.loc['j'] = [7.480703395137295,0.14284429977557123,1.0600936940126982]
df.loc['k'] = [5.570775744372319,0,0]
df.loc['l'] = [4.358946555449826,0,0]
def create_figure(skip_points=[]):
dfs = df.drop(skip_points)
return px.scatter_3d(dfs, x = 'x_val', y = 'y_val', z = 'z_val')
f= create_figure()
app.layout = html.Div([html.Button('Delete', id='delete'),
html.Button('Clear Selection', id='clear'),
dcc.Graph(id = '3d_scat', figure=f),
html.Div('selected:'),
html.Div(id='selected_points'), #, style={'display': 'none'})),
html.Div('deleted:'),
html.Div(id='deleted_points') #, style={'display': 'none'}))
])
#app.callback(Output('deleted_points', 'children'),
[Input('delete', 'n_clicks')],
[State('selected_points', 'children'),
State('deleted_points', 'children')])
def delete_points(n_clicks, selected_points, delete_points):
print('n_clicks:',n_clicks)
if selected_points:
selected_points = json.loads(selected_points)
else:
selected_points = []
if delete_points:
deleted_points = json.loads(delete_points)
else:
deleted_points = []
ns = [p['pointNumber'] for p in selected_points]
new_indices = [df.index[n] for n in ns if df.index[n] not in deleted_points]
print('new',new_indices)
deleted_points.extend(new_indices)
return json.dumps(deleted_points)
#app.callback(Output('selected_points', 'children'),
[Input('3d_scat', 'clickData'),
Input('deleted_points', 'children'),
Input('clear', 'n_clicks')],
[State('selected_points', 'children')])
def select_point(clickData, deleted_points, clear_clicked, selected_points):
ctx = dash.callback_context
ids = [c['prop_id'] for c in ctx.triggered]
if selected_points:
results = json.loads(selected_points)
else:
results = []
if '3d_scat.clickData' in ids:
if clickData:
for p in clickData['points']:
if p not in results:
results.append(p)
if 'deleted_points.children' in ids or 'clear.n_clicks' in ids:
results = []
results = json.dumps(results)
return results
#app.callback(Output('3d_scat', 'figure'),
[Input('selected_points', 'children'),
Input('deleted_points', 'children')],
[State('deleted_points', 'children')])
def chart_3d( selected_points, deleted_points_input, deleted_points_state):
global f
deleted_points = json.loads(deleted_points_state) if deleted_points_state else []
f = create_figure(deleted_points)
selected_points = json.loads(selected_points) if selected_points else []
if selected_points:
f.add_trace(
go.Scatter3d(
mode='markers',
x=[p['x'] for p in selected_points],
y=[p['y'] for p in selected_points],
z=[p['z'] for p in selected_points],
marker=dict(
color='red',
size=5,
line=dict(
color='red',
width=2
)
),
showlegend=False
)
)
return f
if __name__ == '__main__':
app.run_server(debug=True)
I am trying to create a simple line graph for a webapp using plotly and dash. I want to a line connecting two points. I want one of the point tobe red and the other to be green. Here's what I have so far:
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
graph = dcc.Graph(figure = {
'data' : [
go.Scatter(x = [1,4], y = [2,3], mode = 'lines+markers', opacity = 0.7, marker = {
'size' : 15,
'line' : {'width' : 0.5, 'color' : 'black'}
})
]
})
app.layout = html.Div([graph])
if __name__ == '__main__':
app.run_server(debug = False)
I am running this code in a Jupyter notebook with all the latest packages.
If I run this code, I get the line plot I want, but both the points are blue. I want the point corresponding to (1, 2) to be red and (4, 3) to be green. How can I go about doing this?
Thank you so much in advance!
I believe the accepted solution to this problem is to have 3 different go.scatter() objects in your graph data. One for the line, and one for each marker. Your app would end up looking like this:
import dash
import dash_core_components as dcc
import dash_html_components as html
import plotly.graph_objs as go
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
graph = dcc.Graph(figure = {
'data' : [
go.Scatter(x = [1,4], y = [2,3], mode = 'lines+markers', opacity = 0.7,
marker={'color':["red","green"], "size":15})
]
})
app.layout = html.Div([graph])
if __name__ == '__main__':
app.run_server(debug = False)