I have been working through a scenario of chained callbacks and dynamic data frames, but not creating the results that I would like to achieve.
My objective is to allow for multiple, and clearable selections for each of the dropdowns, while each selection made filters the dataframe (df) into a dynamic dataframe (ddf) that is then used for graphics and insights.
If any selection is made in any order, all other options in the dropdowns are populated with the distinct values from the selection made and multiple selections can still be made.
Ex. If selection is made in option3 = Fall & Winter, then option1 would be limited to A & B, option2 would be limited to January, February, September, October, & December, and option3 would result in 15, 30, 80, 75, 75, & 25.
The issue that I see is that if I choose option3 = Fall, the dynamic dataframe is then limited to that option within the ddf and not the remaining 3 options of Winter, Summer and Spring as in the initial dataframe by the way that I’ve written this.
I feel that I may be going about this entirely wrong and making this more difficult of an item than required.
Any feedback or correction is greatly appreciated!
import pandas as pd
from dash import Dash, dcc, html, Input, Output, dash_table
import dash_bootstrap_components as dbc
d = [{'option1': 'A', 'option2': 'January', 'option3': 'Winter', 'option4': '15'},
{'option1': 'A', 'option2': 'February', 'option3': 'Winter', 'option4': '30'},
{'option1': 'A', 'option2': 'March', 'option3': 'Spring', 'option4': '35'},
{'option1': 'A', 'option2': 'April', 'option3': 'Spring', 'option4': '40'},
{'option1': 'A', 'option2': 'May', 'option3': 'Spring', 'option4': '50'},
{'option1': 'A', 'option2': 'June', 'option3': 'Summer', 'option4': '70'},
{'option1': 'B', 'option2': 'July', 'option3': 'Summer', 'option4': '90'},
{'option1': 'B', 'option2': 'August', 'option3': 'Summer', 'option4': '100'},
{'option1': 'B', 'option2': 'September', 'option3': 'Fall', 'option4': '80'},
{'option1': 'B', 'option2': 'October', 'option3': 'Fall', 'option4': '75'},
{'option1': 'B', 'option2': 'November', 'option3': 'Fall', 'option4': '75'},
{'option1': 'B', 'option2': 'December', 'option3': 'Winter', 'option4': '25'}]
df = pd.DataFrame(d)
option1_list = sorted(df['option1'].unique().tolist())
option2_list = df['option2'].unique().tolist()
option3_list = sorted(df['option3'].unique().tolist())
option4_list = sorted(df['option4'].unique().tolist())
app = Dash(__name__)
app.layout = html.Div([
dbc.Card(
dbc.CardBody([
dbc.Row([
dbc.Col([
html.Div([
dbc.Card(
dbc.CardBody([
html.P("Option 1"),
html.Div([
dcc.Dropdown(id='option1_dropdown',
options=option1_list,
value='All',
placeholder='All',
multi=True,
clearable=True),
],
style={'width': '100%', 'display': 'inline-block'})
])
),
])
]),
dbc.Col([
html.Div([
dbc.Card(
dbc.CardBody([
html.P("Option 2"),
html.Div([
dcc.Dropdown(id='option2_dropdown',
options=option2_list,
value='All',
placeholder='All',
multi=True,
clearable=True),
],
style={'width': '100%', 'display': 'inline-block'})
])
),
])
]),
dbc.Col([
html.Div([
dbc.Card(
dbc.CardBody([
html.P("Option 3"),
html.Div([
dcc.Dropdown(id='option3_dropdown',
options=option3_list,
value='All',
placeholder='All',
multi=True,
clearable=True),
],
style={'width': '100%', 'display': 'inline-block'})
])
),
])
]),
dbc.Col([
html.Div([
dbc.Card(
dbc.CardBody([
html.P("Option 4"),
html.Div([
dcc.Dropdown(id='option4_dropdown',
options=option4_list,
value='All',
placeholder='All',
multi=True,
clearable=True),
],
style={'width': '100%', 'display': 'inline-block'})
])
),
])
]),
], align='center'),
]), color='dark'
),
dbc.Card(
dbc.CardBody([
dbc.Row([
dbc.Col([
html.Div([
html.Div(id='dd-output-container')
])
]),
], align='center'),
]), color='dark'
),
dbc.Card(
dbc.CardBody([
dbc.Row([
dbc.Col([
html.Div([
dash_table.DataTable(
id='table_container',
data=df.to_dict('records')
)
])
]),
], align='center'),
]), color='dark'
)
])
@app.callback(
Output('table_container', 'data'),
Output('option1_dropdown', 'options'),
Output('option2_dropdown', 'options'),
Output('option3_dropdown', 'options'),
Output('option4_dropdown', 'options'),
[Input('option1_dropdown', 'value'),
Input('option2_dropdown', 'value'),
Input('option3_dropdown', 'value'),
Input('option4_dropdown', 'value')])
def set_dropdown_options(value1, value2, value3, value4):
# If value lists are empty or equal to the default of 'All', use the initial df values
if not value1 or value1 == 'All':
value1 = option1_list
if not value2 or value2 == 'All':
value2 = option2_list
if not value3 or value3 == 'All':
value3 = option3_list
if not value4 or value4 == 'All':
value4 = option4_list
ddf = df.query('option1 == @value1 and '
'option2 == @value2 and '
'option3 == @value3 and '
'option4 == @value4',
engine='python')
if len(value1) < len(option1_list):
# print(f'option1_list Line 151: {option1_list}')
options1 = sorted(list(set(option1_list) - set(value1)))
for i in options1:
value1.append(i)
value2 = ddf['option2'].unique().tolist()
value3 = sorted(ddf['option3'].unique().tolist())
value4 = sorted(ddf['option4'].unique().tolist())
if len(value2) < len(option2_list):
options2 = sorted(list(set(option2_list) - set(value2)))
for i in options2:
value2.append(i)
value1 = sorted(ddf['option1'].unique().tolist())
value3 = sorted(ddf['option3'].unique().tolist())
value4 = sorted(ddf['option4'].unique().tolist())
if len(value3) < len(option3_list):
options3 = sorted(list(set(option3_list) - set(value3)))
for i in options3:
value3.append(i)
value1 = sorted(ddf['option1'].unique().tolist())
value2 = ddf['option2'].unique().tolist()
value4 = sorted(ddf['option4'].unique().tolist())
if len(value4) < len(option4_list):
options4 = sorted(list(set(option4_list) - set(value4)))
for i in options4:
value4.append(i)
value1 = sorted(ddf['option1'].unique().tolist())
value2 = ddf['option2'].unique().tolist()
value3 = sorted(ddf['option3'].unique().tolist())
return ddf.to_dict('records'), value1, value2, value3, value4
# ====== Using this as a way to view the selections
@app.callback(
Output('dd-output-container', 'children'),
[Input('option1_dropdown', 'value'),
Input('option2_dropdown', 'value'),
Input('option3_dropdown', 'value'),
Input('option4_dropdown', 'value')])
def selection(value1, value2, value3, value4):
# If value lists are empty or equal to the default of 'All', use the initial df values
if not value1 or value1 == 'All':
value1 = option1_list
if not value2 or value2 == 'All':
value2 = option2_list
if not value3 or value3 == 'All':
value3 = option3_list
if not value4 or value4 == 'All':
value4 = option4_list
return f'Selected Option 1: {value1} Option 2: {value2} Option 3: {value3} Option 4: {value4} '
if __name__ == '__main__':
app.run_server(debug=True, use_reloader=False)