Preventing chained callbacks from switching dropdown to original value dash - python

I have a lengthy section of code for several chained callbacks that stem from a multiple nested dictionary. I have all of the necessary dropdowns and options that I would like to provide. However, whenever I change the 'crop' dropdown in this example to something other than the original option (which is corn) it resets the 'Weighting' dropdown below. Similarly, if I change the 'Weighting' dropdown, it resets the 'Forecast Variable' dropdown to the original option. How can I prevent this? The point of the chained callbacks was so that changing one option would change the data that is plotted, as they are all linked.
I don't think the data is important here? But it functions like this:
final_dict[init_date][model][weight][crop]
The above exact dictionary then would output a dataframe. The columns in the dataframe then would be the 'forecast variable' which will eventually be plotted. If I do need to add data I can try and do that but the dict is VERY big.
Here is the code I have so far. Notice that the graph is empty because I haven't gotten that far yet.
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from pandas import Timestamp
import plotly.graph_objs as go
from dash.dependencies import Input, Output
import dash_bootstrap_components as dbc
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
import pandas as pd
import numpy as np
from datetime import timedelta
import glob
import datetime as dt
import xarray as xr
import os
from PIL import Image
import time
import random
my_dict={}
for i in np.arange(1,17,1):
n=random.randint(1,10)
m=random.randint(1,10)
data=[[pd.Timestamp('2020-10-06'),n,m],[pd.Timestamp('2020-10-07'),m,n],[pd.Timestamp('2020-10-08'),n,m],[pd.Timestamp('2020-10-09'),m,n]]
my_dict[i]=pd.DataFrame(data=data, columns=['time', 'Temp','Precip'])
final_dict={'day1':{'model1':{'weight1':{'crop1':my_dict[1], 'crop2':my_dict[2]},
'weight2':{'crop1':my_dict[3], 'crop2':my_dict[4]}},
'model2':{'weight1':{'crop1':my_dict[5], 'crop2':my_dict[6]},
'weight2':{'crop1':my_dict[7], 'crop2':my_dict[8]}}},
'day2':{'model1':{'weight1':{'crop1':my_dict[9], 'crop2':my_dict[10]},
'weight2':{'crop1':my_dict[11], 'crop2':my_dict[12]}},
'model2':{'weight1':{'crop1':my_dict[13], 'crop2':my_dict[14]},
'weight2':{'crop1':my_dict[15], 'crop2':my_dict[16]}}}}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
controls = dbc.Card(
[ dbc.FormGroup(
[dbc.Label("Init Date"),
dcc.Dropdown(
id='init_dd',
options=[{'label': k, 'value': k} for k in final_dict.keys()],
value=list(final_dict.keys())[0],
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Model"),
dcc.Dropdown(
id='model_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Crop"),
dcc.Dropdown(
id='crop_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Weighting"),
dcc.Dropdown(
id='weight_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Forecast Variable"),
dcc.Dropdown(
id='columns_dd',
clearable=False,
),
]
),
],
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
],xs = 2)
,
dbc.Col([
dbc.Row([
dbc.Col([html.Div(id = 'plot_title')],)
]),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
])
])
],),
],
fluid=True,
)
# Callbacks #####################################################################
#set the model
#app.callback(
Output('model_dd', 'options'),
[Input('init_dd', 'value')])
def set_model_options(model):
return [{'label': i.replace('_',' '), 'value': i} for i in final_dict[model]]
#app.callback(
Output('model_dd', 'value'),
[Input('model_dd', 'options')])
def set_model_options_value(available_model):
return available_model[0]['value']
#set the weight
#app.callback(
Output('weight_dd', 'options'),
[Input('init_dd', 'value'),
Input('model_dd', 'value')])
def set_weight_options(selected_init, selected_model):
return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model]]
#app.callback(
Output('weight_dd', 'value'),
[Input('weight_dd', 'options')])
def set_weight_value(available_weight):
return available_weight[0]['value']
#set the crop
#app.callback(
Output('crop_dd', 'options'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value')])
def set_crop_options(selected_init, selected_model, selected_weight):
return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model][selected_weight]]
#app.callback(
Output('crop_dd', 'value'),
[Input('crop_dd', 'options')])
def set_crop_value(available_crop):
return available_crop[0]['value']
#set the variable
#app.callback(
Output('columns_dd', 'options'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value')])
def set_column_options(selected_init, selected_model, selected_weight, selected_crop):
return [{'label': i, 'value': i} for i in final_dict[selected_init][selected_model][selected_weight][selected_crop].columns[1:]]
#app.callback(
Output('columns_dd', 'value'),
[Input('columns_dd', 'options')])
def set_column_value(available_column):
return available_column[1]['value']
app.run_server(mode='external', port = 8099)
Edit: Added in sample dummy data. Notice how when changing certain combinations of options, other options switch back to the original value. Would like to prevent that from happening.

The specific data example helped. I see that
datasets are stored in nested dictionary
you want to allow the user to select a particular dataset (for which each user-input option depends on the previous/upstream selections in the nested structure).
Because the nested structure here means that for a given input change, you want to update the input options only for the subsequent/downstream inputs.
About your issue with better controlling chained callbacks, I think it's a matter of using Input() and State() in right places.
Try this (I renamed your final_dict so that it is easier to monitor what's going on):
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from pandas import Timestamp
import plotly.graph_objs as go
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
import pandas as pd
import numpy as np
from datetime import timedelta
import glob
import datetime as dt
import xarray as xr
import os
from PIL import Image
import time
import random
my_dict={}
for i in np.arange(1,17,1):
n=random.randint(1,10)
m=random.randint(1,10)
data=[[pd.Timestamp('2020-10-06'),n,m],[pd.Timestamp('2020-10-07'),m,n],[pd.Timestamp('2020-10-08'),n,m],[pd.Timestamp('2020-10-09'),m,n]]
my_dict[i]=pd.DataFrame(data=data, columns=['time', 'Temp','Precip'])
final_dict={'day1':{'model1':{'weight1':{'crop1':my_dict[1], 'cropA':my_dict[2]},
'weight2':{'crop2':my_dict[3], 'cropB':my_dict[4]}},
'model2':{'weight3':{'crop3':my_dict[5], 'cropC':my_dict[6]},
'weight4':{'crop4':my_dict[7], 'cropD':my_dict[8]}}},
'day2':{'model3':{'weight5':{'crop5':my_dict[9], 'cropE':my_dict[10]},
'weight6':{'crop6':my_dict[11], 'cropF':my_dict[12]}},
'model4':{'weight7':{'crop7':my_dict[13], 'cropG':my_dict[14]},
'weight8':{'crop8':my_dict[15], 'cropH':my_dict[16]}}}}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
controls = dbc.Card(
[ dbc.FormGroup(
[dbc.Label("Init Date"),
dcc.Dropdown(
id='init_dd',
options=[{'label': k, 'value': k} for k in final_dict.keys()],
value=list(final_dict.keys())[0],
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Model"),
dcc.Dropdown(
id='model_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Crop"),
dcc.Dropdown(
id='crop_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Weighting"),
dcc.Dropdown(
id='weight_dd',
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Forecast Variable"),
dcc.Dropdown(
id='columns_dd',
clearable=False,
),
]
),
],
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
],xs = 2)
,
dbc.Col([
dbc.Row(html.Div(id='selected_data')),
# dbc.Row([
# dbc.Col([html.Div(id = 'plot_title')],)
# ]),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
])
])
],),
],
fluid=True,
)
# Callbacks #####################################################################
#set the model
#app.callback(
Output('model_dd', 'options'),
[Input('init_dd', 'value')])
def set_model_options(model):
return [{'label': i.replace('_',' '), 'value': i} for i in final_dict.get(model).keys()]
#app.callback(
Output('model_dd', 'value'),
[Input('model_dd', 'options')])
def set_model_options_value(available_model):
return available_model[0]['value']
#set the weight
#app.callback(
Output('weight_dd', 'options'),
[Input('model_dd', 'value')],
[State('init_dd', 'value')])
def set_weight_options(selected_model, selected_init):
if selected_model is None: return None
print('selected_model(): ', selected_init, selected_model)
return [{'label': i, 'value': i} for i in final_dict.get(selected_init).get(selected_model).keys()]
#app.callback(
Output('weight_dd', 'value'),
[Input('weight_dd', 'options')])
def set_weight_value(available_weight):
return available_weight[0]['value']
#set the crop
#app.callback(
Output('crop_dd', 'options'),
[Input('weight_dd', 'value')],
[State('init_dd', 'value'),
State('model_dd', 'value')])
def set_crop_options(selected_weight, selected_init, selected_model):
if selected_model is None or selected_weight is None: return None
print('set_crop_options(): ',selected_init, selected_model, selected_weight)
return [{'label': i, 'value': i} for i in final_dict.get(selected_init).get(selected_model).get(selected_weight).keys()]
#app.callback(
Output('crop_dd', 'value'),
[Input('crop_dd', 'options')])
def set_crop_value(available_crop):
return available_crop[0]['value']
#set the variable
#app.callback(
Output('columns_dd', 'options'),
[Input('crop_dd', 'value')],
[State('init_dd', 'value'),
State('model_dd', 'value'),
State('weight_dd', 'value')])
def set_column_options(selected_crop, selected_init, selected_model, selected_weight):
if selected_crop is None or selected_weight is None or selected_model is None: return None
print('set_column_options(): ', selected_init, selected_model, selected_weight, selected_crop)
return [{'label': i, 'value': i} for i in final_dict.get(selected_init).get(selected_model).get(selected_weight).get(selected_crop).columns[1:]]
#app.callback(
Output('columns_dd', 'value'),
[Input('columns_dd', 'options')])
def set_column_value(available_column):
if available_column is None: return None
return available_column[1]['value']
#app.callback(
Output('selected_data', 'children'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value'),
Input('columns_dd','value')]
)
def show_data(init_dd, model_dd, weight_dd, crop_dd, columns_dd):
if crop_dd is None or weight_dd is None or model_dd is None or columns_dd is None: return None
print('show_data():', init_dd, model_dd, weight_dd, crop_dd, columns_dd)
try:
data = final_dict[init_dd][model_dd][weight_dd][crop_dd][columns_dd].to_json(orient='split')
except:
return
return data
def make_plot(df, var):
fig = go.Figure(
data=[go.Scatter(x=df['time'], y=df[var], name=var)],
layout={
'yaxis': {'title': f'Plot of <b>{var}</b>'}
}
)
return fig
no_data_fig = {"layout": {
"xaxis": { "visible": False},
"yaxis": {"visible": False},
"annotations": [
{ "text": "",
"xref": "paper",
"yref": "paper",
"showarrow": False,
"font": {"size": 20 }
}]
}
}
#app.callback(
Output('crop-graph', 'figure'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value'),
Input('columns_dd','value')]
)
def plot_data(init_dd, model_dd, weight_dd, crop_dd, columns_dd):
if crop_dd is None or weight_dd is None or model_dd is None or columns_dd is None: return None
print('plot_data():', init_dd, model_dd, weight_dd, crop_dd, columns_dd)
try:
data = final_dict[init_dd][model_dd][weight_dd][crop_dd]
data_col = data[columns_dd]
except:
return no_data_fig
return make_plot(data, columns_dd)
app.run_server(mode='external', port = 8098, debug=True)

Here is an another version.
I noticed that you wanted to keep the data column column_dd to be fixed without being updated (maybe assumeing the identical columns in the final dataset across different versions). So, I commented out the callback for updating column_dd.
You can also combine outputs as a list.
I tried to show a way to use dynamic input generation but ended up not needing to dynamically update it (I couldn't specify the same output-id twice, which was inconvenient. I just kept this for a demo, and you don't need to switch to this style.) Note that it is still possible to use it as a State() in a callback and overwrite its properties).
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from pandas import Timestamp
import plotly.graph_objs as go
from dash.dependencies import Input, Output, State
import dash_bootstrap_components as dbc
import numpy as np
from plotly.subplots import make_subplots
import plotly.express as px
import pandas as pd
import numpy as np
from datetime import timedelta
import glob
import datetime as dt
import xarray as xr
import os
from PIL import Image
import time
import random
my_dict={}
for i in np.arange(1,17,1):
n=random.randint(1,10)
m=random.randint(1,10)
data=[[pd.Timestamp('2020-10-06'),n,m],[pd.Timestamp('2020-10-07'),m,n],[pd.Timestamp('2020-10-08'),n,m],[pd.Timestamp('2020-10-09'),m,n]]
my_dict[i]=pd.DataFrame(data=data, columns=['time', 'Temp','Precip'])
final_dict={'day1':{'model1':{'weight1':{'crop1':my_dict[1], 'cropA':my_dict[2]},
'weight2':{'crop2':my_dict[3], 'cropB':my_dict[4]}},
'model2':{'weight3':{'crop3':my_dict[5], 'cropC':my_dict[6]},
'weight4':{'crop4':my_dict[7], 'cropD':my_dict[8]}}},
'day2':{'model3':{'weight5':{'crop5':my_dict[9], 'cropE':my_dict[10]},
'weight6':{'crop6':my_dict[11], 'cropF':my_dict[12]}},
'model4':{'weight7':{'crop7':my_dict[13], 'cropG':my_dict[14]},
'weight8':{'crop8':my_dict[15], 'cropH':my_dict[16]}}}}
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
# Helpers #####################################################################
def get_dict_keys(varlist):
dic = final_dict
for var in varlist:
dic = dic.get(var)
return dic
def make_options(option_iter):
return [{'label': i, 'value': i} for i in option_iter]
class InputContainer:
def __init__(self, input_dd='day1', model_dd='model1', weight_dd='weight1',
crop_dd='crop1', columns_dd='Precip'):
self.container = [
dbc.FormGroup(
[dbc.Label("Init Date"),
dcc.Dropdown(
id='init_dd',
options= make_options(final_dict.keys()),
value=input_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Model"),
dcc.Dropdown(
id='model_dd',
value = model_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Weighting"),
dcc.Dropdown(
id='weight_dd',
value = weight_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Crop"),
dcc.Dropdown(
id='crop_dd',
value = crop_dd,
clearable=False,
),
]
),
dbc.FormGroup(
[dbc.Label("Forecast Variable"),
dcc.Dropdown(
id='columns_dd',
value = columns_dd,
options = make_options(['time', 'Temp','Precip']),
clearable=False,
),
]
),
]
self.assign_index()
def assign_index(self): # unused: just for an idea
self.idx = {}
for i, item in enumerate(self.container):
if hasattr(item.children[1], 'id'): # skip the label at 0
self.idx[item.children[1].id] = i
def update_options(self, varname, options, selected=0): # unused: just for an idea
pos = self.idx[varname]
print(self.container[pos].children[1])
if hasattr(self.container[pos].children[1],'id'):
setattr(self.container[pos].children[1],'options', options)
setattr(self.container[pos].children[1],'value', options[selected].get('value'))
# UI #####################################################################
controls = dbc.Card(
html.Div(
id='dynamic-input-container',
children = InputContainer().container),
body=True,
)
app.layout = dbc.Container(
[
html.Hr(),
dbc.Row([
dbc.Col([
dbc.Row([
dbc.Col(controls)
], align="start"),
],xs = 2)
,
dbc.Col([
dbc.Row(html.Div(id='selected_data')),
dbc.Row([
dbc.Col(dcc.Graph(id="crop-graph")),
])
])
],),
],
fluid=True,
)
# Callbacks #####################################################################
#set the model
#app.callback(
[Output('model_dd', 'options'),
Output('model_dd', 'value')],
[Input('init_dd', 'value')],
)
def update_model_options(input_dd):
print('update_model_options():')
options = make_options(get_dict_keys([input_dd]).keys())
return options, options[0].get('value')
#set the weight
#app.callback(
[Output('weight_dd', 'options'),
Output('weight_dd', 'value')],
[Input('model_dd', 'value')],
[State('init_dd', 'value')])
def update_weight_options(model_dd, input_dd):
print('update_weight_options():')
options = make_options(get_dict_keys([input_dd, model_dd]).keys())
return options, options[0].get('value')
#set the crop
#app.callback(
[Output('crop_dd', 'options'),
Output('crop_dd', 'value')],
[Input('weight_dd', 'value')],
[State('init_dd', 'value'),
State('model_dd', 'value')])
def update_crop_options(weight_dd, input_dd, model_dd):
print('update_crop_options():')
options = make_options(get_dict_keys([input_dd, model_dd, weight_dd]).keys())
return options, options[0].get('value')
# #set the variable
# #app.callback(
# [Output('columns_dd', 'options'),
# Output('columns_dd','value')],
# [Input('crop_dd', 'value')],
# [State('init_dd', 'value'),
# State('model_dd', 'value'),
# State('weight_dd', 'value')])
# def set_column_options(crop_dd, input_dd, model_dd, weight_dd):
# print('update_column_options():')
# options = make_options(get_dict_keys([input_dd, model_dd, weight_dd, crop_dd]).columns[1:])
# return options, options[0].get('value')
def make_plot(df, var):
fig = go.Figure(
data=[go.Scatter(x=df['time'], y=df[var], name=var)],
layout={
'yaxis': {'title': f'Plot of <b>{var}</b>'}
}
)
return fig
no_data_fig = {"layout": {
"xaxis": { "visible": False},
"yaxis": {"visible": False},
"annotations": [
{ "text": "",
"xref": "paper",
"yref": "paper",
"showarrow": False,
"font": {"size": 20 }
}]
}
}
#app.callback(
Output('crop-graph', 'figure'),
[Input('init_dd', 'value'),
Input('model_dd', 'value'),
Input('weight_dd', 'value'),
Input('crop_dd', 'value'),
Input('columns_dd','value')]
)
def plot_data(init_dd, model_dd, weight_dd, crop_dd, columns_dd):
if crop_dd is None or weight_dd is None or model_dd is None or columns_dd is None: return None
print('plot_data():', init_dd, model_dd, weight_dd, crop_dd, columns_dd)
try:
data = final_dict[init_dd][model_dd][weight_dd][crop_dd]
data_col = data[columns_dd]
except:
return no_data_fig
return make_plot(data, columns_dd)
app.run_server(mode='external', port = 8098, debug=True)

Related

Display interactive metrics within dbc.Card - Dash

I'm aiming to include interactive counts as metrics. Specifically, insert total sums within cards that will changed depending on the variable selected from a nav bar.
Using below, the Navbar is used to display the proportion of successes and failures as a pie chart. I want to use the corresponding sums of success_count and failure_count as a metric to be displayed as a number.
Is it possible to return these values and display them within the designated dbc.Card?
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objs as go
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
# This dataframe has 244 lines, but 4 distinct values for `day`
url="https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBM-DS0321EN-SkillsNetwork/datasets/spacex_launch_dash.csv"
spacex_df = pd.read_csv(url)
spacex_df.rename(columns={'Launch Site':'Site'}, inplace=True)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
success_card = dbc.Card(
[
dbc.CardHeader("Some header"),
dbc.CardBody(
[
html.H6("Success Count", className="card-title"),
html.P("###total count of successes", className="card-text"),
]
),
],
className='text-center m-4'
)
failure_card = dbc.Card(
[
dbc.CardHeader("Some header"),
dbc.CardBody(
[
html.H6("Failure Count", className="card-title"),
html.P("##total count of failures", className="card-text"),
]
),
],
className='text-center m-4'
)
nav_bar = html.Div([
html.P("site-dropdown:"),
dcc.Dropdown(
id='Site',
value='Site',
options=[{'value': x, 'label': x}
for x in ['CCAFS LC-40', 'CCAFS SLC-40', 'KSC LC-39A', 'VAFB SLC-4E']],
clearable=False
),
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar, className="bg-secondary h-100"), width=2),
dbc.Col([
dbc.Row([
dbc.Col(success_card),
]),
dbc.Row([
dbc.Col(dcc.Graph(id = 'pie-chart'), style={
"padding-bottom": "10px",
},),
]),
dbc.Row([
# insert pie chart
dbc.Col(dcc.Graph(id = "bar-chart")),
]),
], width=5),
dbc.Col([
dbc.Row([
dbc.Col(failure_card),
]),
dbc.Row([
# insert bar chart
dbc.Col(dcc.Graph()),
], className="h-100"),
], width=5),
])
], fluid=True)
#app.callback(
Output("pie-chart", "figure"),
[Input("Site", "value")])
def generate_chart(value):
pie_data = spacex_df[spacex_df['Site'] == value]
success_count = sum(pie_data['class'] == 0)
failure_count = sum(pie_data['class'] == 1)
fig = go.Figure(data=[go.Pie(labels=['success','failure'], values=[success_count, failure_count])])
fig.update_layout(title=f"Site: {value}")
return fig
if __name__ == '__main__':
app.run_server(debug=True)
You should use html.Div in your card with id and then return html.P in this Div. Something as below:
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import plotly.express as px
import plotly.graph_objs as go
import dash
from dash import dcc
from dash import html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output
# This dataframe has 244 lines, but 4 distinct values for `day`
url="https://cf-courses-data.s3.us.cloud-object-storage.appdomain.cloud/IBM-DS0321EN-SkillsNetwork/datasets/spacex_launch_dash.csv"
spacex_df = pd.read_csv(url)
spacex_df.rename(columns={'Launch Site':'Site'}, inplace=True)
external_stylesheets = [dbc.themes.SPACELAB, dbc.icons.BOOTSTRAP]
app = dash.Dash(__name__, external_stylesheets = external_stylesheets)
success_card = dbc.Card(
[
dbc.CardHeader("Some header"),
dbc.CardBody(
[
html.H6("Success Count", className="card-title"),
html.Div(id='count_of_success'),
]
),
],
className='text-center m-4'
)
failure_card = dbc.Card(
[
dbc.CardHeader("Some header"),
dbc.CardBody(
[
html.H6("Failure Count", className="card-title"),
html.Div(id='count_of_failure'),
]
),
],
className='text-center m-4'
)
nav_bar = html.Div([
html.P("site-dropdown:"),
dcc.Dropdown(
id='Site',
value='Site',
options=[{'value': x, 'label': x}
for x in ['CCAFS LC-40', 'CCAFS SLC-40', 'KSC LC-39A', 'VAFB SLC-4E']],
clearable=False
),
])
app.layout = dbc.Container([
dbc.Row([
dbc.Col(html.Div(nav_bar, className="bg-secondary h-100"), width=2),
dbc.Col([
dbc.Row([
dbc.Col(success_card),
]),
dbc.Row([
dbc.Col(dcc.Graph(id = 'pie-chart'), style={
"padding-bottom": "10px",
},),
]),
dbc.Row([
# insert pie chart
dbc.Col(dcc.Graph(id = "bar-chart")),
]),
], width=5),
dbc.Col([
dbc.Row([
dbc.Col(failure_card),
]),
dbc.Row([
# insert bar chart
dbc.Col(dcc.Graph()),
], className="h-100"),
], width=5),
])
], fluid=True)
#app.callback(
[Output("pie-chart", "figure"),
Output("count_of_success", "children"),
Output("count_of_failure", "children")],
[Input("Site", "value")])
def generate_chart(value):
pie_data = spacex_df[spacex_df['Site'] == value]
success_count = sum(pie_data['class'] == 0)
failure_count = sum(pie_data['class'] == 1)
fig = go.Figure(data=[go.Pie(labels=['success','failure'], values=[success_count, failure_count])])
fig.update_layout(title=f"Site: {value}")
return fig, html.P(f'###total count of successes are: {success_count}'), html.P(f'###total count of failure are: {failure_count}')
if __name__ == '__main__':
app.run_server(debug=False)
And here is the result:

Plotly chained callback in Python

I would like to create a heat map for different type of users ("segment"), and different day (date)
My code doesn't work and I don't know why. This is the error:
An object was provided as `children` instead of a component, string, or number (or list of those). Check the children property that looks something like:
{
"label": "Mieszkaniec",
"value": "Mieszkaniec"
}
I think the problem is in the layout. Can someone explain me how exactly children work in this example? Maybe it is impossible to do a chained callback for map
import pandas as pd import dash import plotly.express as px import plotly.graph_objects as go from dash import Dash, dcc, html, Input, Output
dane = pd.read_csv("C:\\Users\\fickar00\\Desktop\\Dane\\test2.csv",
encoding = "unicode_escape", sep=";", na_values='-') dane = dane.fillna(0) dane["c_users"] = [float(str(i).replace(",", "")) for i in dane["c_users"]] dane = dane.astype({"c_users": float})
app = Dash(__name__)
app.layout = html.Div([
html.H1('Dobowe przemieszcznie się ludności w Łodzi w interwale godzinowym'),
html.Label("Wybierz segment:"),
dcc.Dropdown(id="wybierz_segment",
options=[
{"label": "Mieszkaniec", "value": "Mieszkaniec"},
{"label": "Regularny gość", "value": "Regularny_gosc"},
{"label": "Turysta", "value": "Turysta"}],
multi=False,
value="Mieszkaniec",
clearable=False,
style={'width': "40%"}
),
html.Label("Wybierz date:"),
dcc.Dropdown(id='wybierz_date',
options=[{'label': s, 'value': s} for s in sorted(dane.day.unique())],
value="01.09.2022",
style={'width': "40%"},
multi=False),
dcc.Graph(id='mapa', figure={})
]) #app.callback(
Output('wybierz_date', 'options'),
Input('wybierz_segment', 'value') ) def ustaw_opcje_segmentu(wybrany_segment):
dff = dane[dane.segment==wybrany_segment]
return [{'label': c, 'value': c} for c in sorted(dff.segment.unique())]
# populate initial values of counties dropdown #app.callback(
Output('wybierz_date', 'value'),
Input('wybierz_date', 'options') ) def ustaw_wartosci_segmentu(dostepne_opcje):
return [x['value'] for x in dostepne_opcje]
#app.callback(
Output('mapa', 'figure'),
Input('wybierz_date', 'value'),
Input('wybierz_segment', 'value') ) def update_grpah(wytypowany_segment, wytypowana_data):
if len(wytypowana_data) == 0:
return dash.no_update
else:
dff = dane[(dane.segment==wytypowany_segment) & (dane.day==wytypowana_data)]
fig = px.density_mapbox(dff,
lat='loc_wgs84_lat',
lon='loc_wgs84_lon',
animation_frame='hour',
animation_group='c_users',
zoom=10,
z='c_users',
height=650,
width=1400,
mapbox_style="carto-darkmatter")
return fig
#fig.write_html("C:\\Karol\\Python\\file.html")
if __name__ == '__main__':
app.run_server(debug=True)

Sidebar with filters (dropdowns) in Plotly Dash

I am trying to program a sidebar with some filters but the callback update_output of this dropdown is not working (not throwing an error, just doing nothing).
I think it is because this dropdown is not in the main layout, because my layout is composed of the sidebar and a content. My dropdowns in this sidebar will be filters that will be applied to the dataframe that feeds the graphs of the dashboard and dashboard2 layouts.
My question is how or where should I program those callbacks to give functionality to my sidebar dropdowns?
sidebar = html.Div(
[
html.H2("Sidebar", className="display-4"),
html.Hr(),
html.P(
"Sidebar", className="lead"
),
dbc.Nav(
[
dbc.NavLink("Home", href="/", active="exact"),
dbc.NavLink("Page 1", href="/page-1", active="exact"),
dbc.NavLink("Page 2", href="/page-2", active="exact"),
],
vertical=True,
pills=True,
),
dcc.Dropdown(id='dropdown',value="City"),
html.Br(),
dcc.Dropdown(id='dropdown2',options=[])
],
style=SIDEBAR_STYLE,
)
content = html.Div(id="page-content", children=[], style=CONTENT_STYLE)
app.layout = html.Div([
dcc.Location(id="url"),
sidebar,
content
])
#app.callback(
Output('dropdown2', 'options'),
Input('dropdown', 'value')
)
def update_output(value):
return df[df["cities"].isin(value)]
#app.callback(
Output("page-content", "children"),
[Input("url", "pathname")]
)
def render_page_content(pathname):
if pathname == "/":
return dashboard.layout
elif pathname == "/page-1":
return dashboard.layout
elif pathname == "/page-2":
return dashboard2.layout
# return a 404 message when user tries to reach a different page
return dbc.Jumbotron(
[
html.H1("404: Not found", className="text-danger"),
html.Hr(),
html.P(f"The pathname {pathname} was not recognised..."),
]
)
if __name__=='__main__':
app.run_server(debug=True, port=8000)
I find checklists to be better for this purpose. If you're open to using that instead of dropdown menu to filter your dataframe, then the complete snippet below will produce the following Plotly Dash App.
Complete code:
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output, State, ClientsideFunction
import dash_bootstrap_components as dbc
import dash_core_components as dcc
import pandas as pd
import plotly.graph_objs as go
import numpy as np
import plotly.express as px
app = JupyterDash(external_stylesheets=[dbc.themes.SLATE])
df = px.data.stocks()
df = df.set_index('date')
controls = dbc.Card(
[
dbc.FormGroup(
[
dbc.Label("Checklist"),
dcc.Checklist(
id="column_select",
options=[{"label":col, "value": col} for col in df.columns],
value=[df.columns[0]],
labelStyle={'display': 'inline-block', 'width': '12em', 'line-height':'0.5em'}
),
],
),
],
body=True,
style = {'font-size': 'large'}
)
app.layout = dbc.Container(
[
html.H1("Dropdowns and checklists"),
html.Hr(),
dbc.Row([
dbc.Col([controls],xs = 4),
dbc.Col([
dbc.Row([
dbc.Col(dcc.Graph(id="graph"), style={'height': '420px'}),
])
]),
]),
],
fluid=True,
)
#app.callback(
Output("graph", "figure"),
[Input("column_select", "value"),],
)
def make_graph(cols):
fig = px.line(df, x = df.index, y = cols, template = 'plotly_dark')
return fig
app.run_server(mode='external', port = 8982)

Plotly Dash Update Drop Down from call back where data frame inside the call back

I am trying to update the drop-down menu from the data frame column value where my data frame is generated inside call back since I am taking user inputs and extracting some data from API.
I want to create a filter in the dropdown that's why I want that drop-down updated dynamically using the data frame column.
so in a call back I have df['name_id'] through which I want to update the segment dropdown.
I have taken the only important line to break in my code since the code is too lengthy:
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
import datetime
import dash_table
import httplib2
import pandas as pd
import requests
import io
import time
from Dash_App.app import app
from dash.dependencies import Input, Output, State
from oauth2client import GOOGLE_REVOKE_URI, GOOGLE_TOKEN_URI, client
from oauth2client import client, GOOGLE_TOKEN_URI
from datetime import date, timedelta
from apiclient import discovery
row1 = html.Div(
[
dbc.Row([
dbc.Col([
dbc.Input(id="client_id",
type="text",
placeholder="Client ID",
style={'width': '150px'}, persistence=True,
persistence_type='memory'),
]),
], align="center"),
], style={'margin-top': 20, 'margin-left': -90}
)
row2 = html.Div([
dbc.Row([
dbc.Col([
dcc.Dropdown(
id='segment',
options=[{'label': i, 'value': i} for i in "what"],
multi=True,
style={'width': '250px'},
placeholder='Segment'),
]),
])
])
tab_2_layout = dbc.Container(children=[
row1,
html.Br(),
row2,
]
)
#app.callback(Output('output_div-ga', 'children'),
[Input('submit-button', 'n_clicks')],
[State('client_id', 'value'),
],
)
def ga_output(clicks, client_id):
if clicks is not None:
my_client_id = client_id
credentials = client.OAuth2Credentials(
access_token=None, # set access_token to None since we use a refresh token
client_id=my_client_id)
credentials.refresh(httplib2.Http()) # refresh the access token (optional)
http = credentials.authorize(httplib2.Http()) # apply the credentials
service_v3 = discovery.build('analytics', 'v3', http=http)
segments = service_v3.management().segments().list().execute()
df = pd.DataFrame(segments['items'])
df = df[['name', 'id']]
df['name_id'] = df.name.astype(str).str.cat(df.id.astype(str), sep=':')
return html.Div([dcc.Store('memory'),
dash_table.DataTable(
id='table',
columns=[{"name": i, "id": i} for i in dff.columns],
data=dff.to_dict("rows"), persistence=True, persistence_type='memory',
export_format="csv",
),
])
I am able to solve this by generating form inside call back.
return html.Div([
dbc.Row([
dbc.Col([
dcc.Dropdown(
id='segment',
options=[{'label': i, 'value': i} for i in df['name_id'].unique()],
persistence=True, persistence_type='memory',
multi=True,
style={'width': '250px', 'margin-left': -250, 'margin-top': 10},
placeholder='Segment'),
]), ]), ])
What worked for me was to give each column that you want to edit an id, make it editable and then update the dropdown options in the DataTable. I also returned the whole DataTable in my callback.
app.callback(
Output("table-dropdown-container", "children"),
Input("input-data-table-id", "data"),
)(self.update_unlabelled_shops_table)
...
dt = (
dash_table.DataTable(
id='table-id',
data=df.to_dict("records"),
columns=[{"name": i, "id": i} if i != column_name else {"name": i, "id": i, "editable": True, "presentation": "dropdown"} for i in df.columns],
page_size=8,
dropdown={
column_name: {
"options": [
{"label": i, "value": i}
for i in df[column_name].unique()
]
},
},
),
)
return dt
except Exception as e:
print(e)
return [None]
And to create your div block.
def create_div(self):
return html.Div(
[
dash_table.DataTable(
id='table-id',
columns=[],
editable=True,
dropdown={},
),
html.Div(id="table-dropdown-container"),
]
)
Hope this helps somebody...

save a modified dash datatable to dataframe

I'm new to dash and I'm struggling to save the edits made on the dash data table back to a data frame
here is my code of the data table
import dash
from dash.dependencies import Input, Output, State
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import sqlalchemy
app = dash.Dash(__name__)
engine = sqlalchemy.create_engine('mysql+pymysql://root:#127.0.0.1:3306/sfp')
df = pd.read_sql_table("base_case",engine)
app.layout = html.Div([
html.Div([
dcc.Input(
id='adding-rows-name',
placeholder='Enter a column name...',
value='',
style={'padding': 10}
),
html.Button('Add Column', id='adding-rows-button', n_clicks=0)
], style={'height': 50}),
dash_table.DataTable(
id='adding-rows-table',
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict('records'),
editable=True,
row_deletable=True
),
html.Button('Add Row', id='editing-rows-button', n_clicks=0),
])
if __name__ == '__main__':
app.run_server(debug=True)
is there any solution to do that ?
You need a callback for it and this is an example how you could do it:
import dash
import dash_table
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
app = dash.Dash(__name__)
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/solar.csv')
nmb_clicks = 0
app.layout = html.Div([
dcc.Store(id='click-memory', data = {'nmb_clicks': nmb_clicks}),
html.Div([
dcc.Input(
id='adding-rows-name',
placeholder='Enter a column name...',
value='',
style={'padding': 10}
),
html.Button('Add Column', id='adding-columns-button', n_clicks=nmb_clicks)
], style={'height': 50}),
dash_table.DataTable(
id='adding-rows-table',
columns=[{"name": i, "id": i} for i in df.columns],
data=df.to_dict('records'),
editable=True,
row_deletable=True
),
html.Button('Add Row', id='editing-rows-button', n_clicks=0),
])
#app.callback(dash.dependencies.Output('adding-rows-table', 'columns'),
[dash.dependencies.Input('adding-columns-button', 'n_clicks'),
dash.dependencies.Input('adding-rows-name', 'value')],
[dash.dependencies.State('click-memory', 'data')])
def update_dropdown(click, name, data):
if click != data['nmb_clicks']:
if name not in df.columns:
df[name] = [float('nan')] * len(df.index)
return [{"name": i, "id": i} for i in df.columns]
#app.callback(dash.dependencies.Output('click-memory', 'data'),
[dash.dependencies.Input('adding-columns-button', 'n_clicks')],
[dash.dependencies.State('click-memory', 'data')])
def on_data(click, data):
if click != nmb_clicks:
data['nmb_clicks'] = data['nmb_clicks'] + 1
return data
if __name__ == '__main__':
app.run_server(debug=True)
Note that you would need to fill the column with some meaningful values (now empty cell are being inserted).

Categories