Dash App With Multiple Inputs (dropdowns)

Hello everyone! I have been trying to create an interactive dashboard in python using the @app.callback function with two inputs. My dataset layout can be summarized into 4 main columns.

I’d like Geography and Time Period to manifest in the form of dropdowns (therefore use the Dcc. dropdown function. The first dropdown will filter the dataset according to the Geography and the second one will define the "Period time - MAT, L12w or L4w) within the country. Therefore somehow the second dropdown is to be integrated within the first dropdown.

The most important thing is that the output desired is a pie chart that distinguishes Manufacturers’ (Column 3) value share according to the selected Geography and time Period. I am guessing the mystery resides in either the callback function or the function to deliver the plot. However, I tried everything and the code won’t work. Also, you will find the code I have done so far attached. The important bit is from “#DESIGN APP LAYOUT” onwards. I got the following error: return func(*args, **kwargs) TypeError: Div.init() got multiple values for argument ‘children’
I’d really appreciate a quick response. Thanks in advance for the help!

import dash
from dash import html
from dash import dcc
from dash.dependencies import Input, Output, State
import plotly.express as px
import pandas as pd
import pandas as pd
pd.options.display.max_columns = None
pd.options.display.max_rows = None
pd.options.display.width=None


data =  pd.read_csv (r'C:\Users\Sara.Munoz\OneDrive - Unilever\Documents\Sarita.csv', 
                            encoding = "ISO-8859-1",
                            )
df=data
print(df.head())
cols=df.columns
print(cols)

###RE-ARRANGE DATASET###
df = pd.melt(df, id_vars=['Geography Node Name', 'Geography Id', 'Geography Level',
       'Category Node Name', 'Category Id', 'Category Level',
       'Global Manufacturer Name', 'Global Manufacturer Id',
       'Brand Position Type', 'Brand Position Name', 'Brand Position Id',
       'Local Brand Name', 'Local Brand Id', 'Measure',
       'Currency or Unit of Measure','Latest Available Date'],value_vars=['MAT','L12W','L4W'], var_name='Period',value_name='Data')

for col in df.columns:
    print(col)


###CLEAN DATASET###

df.rename(columns = {'Geography Node Name':'Geography','Category Node Name':'Category',
                     'Global Manufacturer Name':'Manufacturer','Geography Level':'GLevel'},inplace = True)


df.drop(["Geography Id", "Category Id","Global Manufacturer Id","Brand Position Type",
                  "Brand Position Name","Brand Position Id","Local Brand Name","Local Brand Id","Latest Available Date",
         "Currency or Unit of Measure"], axis = 1, inplace=True)

print("SEE BELOW NEW DATASET")

print(df.head())

#####FOR VALUE SHARE

print("FOR VALUE SHARE")

df2 = df.loc[df['GLevel'] == 5]
df2 = df2.loc[df2['Measure'] == 'Value Share']
df2 = df2.loc[df2['Category'] == 'Toothpaste']
df2 = df2[df2.Manufacturer != 'ALL MANUFACTURERS']
df2 = df2[df2.Category != 'Oral Care']
df2.drop(["GLevel", "Category","Category Level"], axis = 1, inplace=True)
print(df2.head())

#####FOR VOLUME SHARE

print("FOR VOLUME SHARE")

df3 = df.loc[df['GLevel'] == 5]
df3 = df3.loc[df3['Measure'] == 'Volume Share']
df3 = df3.loc[df3['Category'] == 'Toothpaste']
df3 = df3[df3.Manufacturer != 'ALL MANUFACTURERS']
df3 = df3[df3.Category != 'Oral Care']
df3.drop(["GLevel", "Category","Category Level"], axis = 1, inplace=True)
df3=df3.sort_values(['Geography', 'Period'],ascending = [True, True])
df3 = pd.DataFrame(df3)
df3=df3[['Geography','Period','Manufacturer','Measure','Data']]
print(df3)

#DESIGN APP LAYOUT##############################################################################

app = dash.Dash(__name__)

app.layout = html.Div(
    [
        dcc.Dropdown(
            id="dropdown-1",
            options=[
                             {'label': 'Indonesia', 'value': 'Indonesia'},
                            {'label': 'France', 'value': 'France'},
                             {'label': 'Vietnam', 'value': 'Vietnam'},
                             {'label': 'Chile', 'value': 'Chile'},
                            {'label': 'United Arab Emirates', 'value': 'United Arab Emirates'},
                             {'label': 'Morocco', 'value': 'Morocco'},
                             {'label': 'Russian Federation', 'value': 'Russian Federation'},
                            {'label': 'China', 'value': 'China'},
                             {'label': 'Greece', 'value': 'Greece'},
                             {'label': 'Netherlands', 'value': 'Netherlands'},
                            {'label': 'Austria', 'value': 'Austria'},
                             {'label': 'Germany', 'value': 'Germany'},
                             {'label': 'Switzerland', 'value': 'Switzerland'},
                            {'label': 'Italy', 'value': 'Italy'},
                             {'label': 'Denmark', 'value': 'Denmark'},
                             {'label': 'Norway', 'value': 'Norway'},
                             {'label': 'Sweden', 'value': 'Sweden'}
        ],
            multi=True,
        ),
        dcc.Dropdown(
            id="dropdown-2",
           options=[
                             {'label': 'MAT', 'value': 'MAT'},
                            {'label': 'L12W', 'value': 'L12W'},
                             {'label': 'L4W', 'value': 'L4W'}
        ],
            multi=True,
        ),
        html.Div([], id="plot1", children=[])
    ], style={'display': 'flex'})


@app.callback(
    Output("plot1", "children"),
    [Input("dropdown-1", "value"), Input("dropdown-2", "value")],
    prevent_initial_call=True
)

def get_graph(entered_Geo, entered_Period):
    
    fd = df2[(df3['Geography']==entered_Geo) &
             (df3['Period']==entered_Period)]

    g1= fd.groupby(['Manufacturer'],as_index=False). \
            mean()
    g1 = g1
    plot1= px.pie(g1, values='Data', names='Manufacturer', title="Value MS")


    return[dcc.Graph(figure=plot1)]

if __name__ == '__main__':
    app.run_server()

Hi @saraarenas
Welcome to the community.

Just to make sure I understood you correctly… You would like the options of the second dropdown (time period) to be dependent on the value chosen from the first dropdown(country)?

1 Like

Hi @adamschroeder

Thanks for the quick answer!

Yes, exactly. Also, do you think it’d be possible to create this final graph in which value share (‘Data’ Column) is displayed according to the manufacturer (‘Manufacturer’ column) - while being able to select the Geography and time period from the two dropdowns?

I see, ok. That is usually referred to as a chained callback. Here’s an example of chained radioitems in the docs.

There are more examples of chained dropdowns on this forum. I recommend trying to implement a simple chained dropdown example by building a new minimal app. And once you get the idea behind it, then try to implement it into your current app.

Hi Adam,

Thanks for the answer. I followed your advice and re-arranged the layout accordingly. I feel I am about to get the desired output but there is still a minor problem with the last part of the code:

app = dash.Dash(__name__)

app.layout = html.Div([
    html.Label("Geography:",style={'fontSize':30, 'textAlign':'center'}),
    dcc.Dropdown(
        id='dropdown1',
        options=[{'label': s, 'value': s} for s in sorted(df3.Geography.unique())],
        value=None,
        clearable=False
    ),
    html.Label("Period:", style={'fontSize':30, 'textAlign':'center'}),
    dcc.Dropdown(id='dropdown2',
                 options=[{'label': s, 'value': s} for s in sorted(df3.Period.unique())],
                 value=[],
                 multi=True),
    html.Div(id='graph-container', children=[])
])

##############
# Populate the Period dropdown with options and values
@app.callback(
    Output('dropdown2', 'value'),
    Output('dropdown2', 'value'),
    Input('dropdown1', 'value'),
)
def set_period_options(chosen_Geo):
    dff = df3[df3.Geography==chosen_Geo]
    Periods = [{'label': s, 'value': s} for s in sorted(df3.Period.unique())]
    values_selected = [x['value'] for x in Periods]
    return Periods, values_selected

# Create graph component and populate with pie chart
@app.callback(
    Output('graph-container', 'children'),
    Input('dropdown2', 'value'),
    State('dropdown1', 'value'),
    prevent_initial_call=True
)
def update_grapah(selected_Period, selected_Geo):
    if len(selected_Period) == 0:
        return no_update

    else:

        dff = df3[(df3.Geography==selected_Geo) & (df3.Period.isin(selected_Period))].
        fig = px.pie(dff, values='Data', names='Manufacturer', title="Value MS")
        return dcc.Graph(id='display-map', figure=fig)

if __name__ == '__main__':
    app.run_server()

I have followed the code from Dash-by-Plotly/fig_postset_jupyterlab.ipynb at master · Coding-with-Adam/Dash-by-Plotly · GitHub.

Issue is: when I create the dff with the selections (according to dropdowns) I would then want to return a pie chart grouping by Manufacturer. So for example say that I select China and “MAT” : I have the value share for 4 Manufacturers. I’d like a pie chart that represents this and updates when I choose say France - L12W, which has 7 instead of 4 suppliers…

hi @saraarenas
it’s a bit challenging without the data or being able to run this locally on our machine. But I think there are a couple of things.

Your code

@app.callback(
    Output('dropdown2', 'value'),
    Output('dropdown2', 'value'),
    Input('dropdown1', 'value'),
)
def set_period_options(chosen_Geo):
    dff = df3[df3.Geography==chosen_Geo]
    Periods = [{'label': s, 'value': s} for s in sorted(df3.Period.unique())]
    values_selected = [x['value'] for x in Periods]
    return Periods, values_selected
  • Since Periods represents the dropdown options, the first output component_property should be ‘options’ (instead of value).
  • And the Periods should refer to the filtered dataframe (dff), not df3.
@app.callback(
    Output('dropdown2', 'options'),
    Output('dropdown2', 'value'),
    Input('dropdown1', 'value'),
)
def set_period_options(chosen_Geo):
    dff = df3[df3.Geography==chosen_Geo]
    Periods = [{'label': s, 'value': s} for s in sorted(dff.Period.unique())]
    values_selected = [x['value'] for x in Periods]
    return Periods, values_selected

Let me know if that makes sense or if you were trying something else with the first callback. Then, we can work on the second callback.

Gotttitt!! Thank


app = dash.Dash(__name__)

app.layout = html.Div([
    html.Label("Geography:",style={'fontSize':30, 'textAlign':'center'}),
    dcc.Dropdown(
        id='dropdown1',
        options=[{'label': s, 'value': s} for s in sorted(df3.Geography.unique())],
        value=None,
        clearable=False
    ),
    html.Label("Period:", style={'fontSize':30, 'textAlign':'center'}),
    dcc.Dropdown(id='dropdown2',
                 options=[],
                 value=[],
                 multi=False),
    html.Div([
                
                        html.Div([ ], id='plot1'),
                        html.Div([ ], id='plot2')

                        
                    ], style={'display': 'flex'}),


    ])

##############
# Populate the Period dropdown with options and values
@app.callback(
    Output('dropdown2', 'options'),
    Output('dropdown2', 'value'),
    Input('dropdown1', 'value'),
)
def set_period_options(chosen_Geo):
    dff = df3[df3.Geography==chosen_Geo]
    Periods = [{'label': s, 'value': s} for s in df3.Period.unique()]
    values_selected = [x['value'] for x in Periods]
    return Periods, values_selected

# Create graph component and populate with pie chart
@app.callback([Output(component_id='plot1', component_property='children'),
               Output(component_id='plot2', component_property='children')],
              Input('dropdown2', 'value'),
              Input('dropdown1', 'value'),
    prevent_initial_call=True
)
def update_graph(selected_Period, selected_Geo):
    if len(selected_Period) == 0:
        return no_update

    else:
        #Volume Share
        dff3 = df3[(df3.Geography==selected_Geo) & (df3.Period==selected_Period)]
        #Value Share
        dff2 = df2[(df2.Geography==selected_Geo) & (df2.Period==selected_Period)]
        #####

        fig1 = px.pie(dff2, values='Data', names='Manufacturer', title=" Value MS")
        fig2 = px.pie(dff3, values='Data', names='Manufacturer', title=" Volume MS")
        table = 
        
        return [dcc.Graph(figure=fig1),
            dcc.Graph(figure=fig2) ]

if __name__ == '__main__':
    app.run_server()```

Gottit!!!, thanks for your time!

The code used:


#DESIGN APP LAYOUT##############################################################################

app = dash.Dash(__name__)

app.layout = html.Div([
    html.Label("Geography:",style={'fontSize':30, 'textAlign':'center'}),
    dcc.Dropdown(
        id='dropdown1',
        options=[{'label': s, 'value': s} for s in sorted(df3.Geography.unique())],
        value=None,
        clearable=False
    ),
    html.Label("Period:", style={'fontSize':30, 'textAlign':'center'}),
    dcc.Dropdown(id='dropdown2',
                 options=[],
                 value=[],
                 multi=False),
    html.Div([
                
                        html.Div([ ], id='plot1'),
                        html.Div([ ], id='plot2')

                        
                    ], style={'display': 'flex'}),


    ])

##############
# Populate the Period dropdown with options and values
@app.callback(
    Output('dropdown2', 'options'),
    Output('dropdown2', 'value'),
    Input('dropdown1', 'value'),
)
def set_period_options(chosen_Geo):
    dff = df3[df3.Geography==chosen_Geo]
    Periods = [{'label': s, 'value': s} for s in df3.Period.unique()]
    values_selected = [x['value'] for x in Periods]
    return Periods, values_selected

# Create graph component and populate with pie chart
@app.callback([Output(component_id='plot1', component_property='children'),
               Output(component_id='plot2', component_property='children')],
              Input('dropdown2', 'value'),
              Input('dropdown1', 'value'),
    prevent_initial_call=True
)
def update_graph(selected_Period, selected_Geo):
    if len(selected_Period) == 0:
        return no_update

    else:
        #Volume Share
        dff3 = df3[(df3.Geography==selected_Geo) & (df3.Period==selected_Period)]
        #Value Share
        dff2 = df2[(df2.Geography==selected_Geo) & (df2.Period==selected_Period)]
        #####

        fig1 = px.pie(dff2, values='Data', names='Manufacturer', title=" Value MS")
        fig2 = px.pie(dff3, values='Data', names='Manufacturer', title=" Volume MS")
        table = 
        
        return [dcc.Graph(figure=fig1),
            dcc.Graph(figure=fig2) ]

if __name__ == '__main__':
    app.run_server()```