Conditional dropdown options

A senior purchasing officer in the e-commerce company has posed an interesting problem. Their existing dashboard, showing sales by month for minor categories of items, has a two-level dropdown (major category and minor category) with way too many options in it. It is especially annoying, you are told, that some options appear in the second dropdown that can not be selected.

You assure the stakeholder you can assist - it is a great opportunity for you to use chained callbacks to produce a conditional dropdown.

Este exercício faz parte do curso

Building Dashboards with Dash and Plotly

Ver Curso

Instruções de exercício

  • Use the major_categories list created for you on line 8 to set up the Major Category options for that dropdown below line 28 with the same value and label for each option.
  • Create a callback triggered by the major category dropdown (major_cat_dd) that updates the minor category dropdown (minor_cat_dd) options to be only those relevant below line 47.
  • Reformat relevant_minor_options (created on line 55) into a list of dictionaries below line 57 so it can be used in the Dash dropdown.
  • Create a callback triggered by the minor category dropdown that will update the line figure (sales_line) upon selection of an option below line 61.

Exercício interativo prático

Experimente este exercício preenchendo este código de exemplo.

import dash
from dash import dcc, html
import plotly.express as px
import pandas as pd
from dash.dependencies import Input, Output
ecom_sales = pd.read_csv('/usr/local/share/datasets/ecom_sales.csv')
major_categories = list(ecom_sales['Major Category'].unique())
minor_categories = list(ecom_sales['Minor Category'].unique())
logo_link = 'https://assets.datacamp.com/production/repositories/5893/datasets/fdbe0accd2581a0c505dab4b29ebb66cf72a1803/e-comlogo.png'
ecom_country = ecom_sales.groupby('Country')['OrderValue'].agg(['sum', 'count']).reset_index().rename(columns={'count':'Sales Volume', 'sum':'Total Sales ($)'})

app = dash.Dash(__name__)

app.layout = html.Div([
  html.Img(src=logo_link, 
        style={'margin':'30px 0px 0px 0px' }),
  html.H1('Sales breakdowns'),
  html.Div(
    children=[
    html.Div(
      children=[
      html.H2('Controls'),
      html.Br(),
      html.H3('Major Category Select'),
      dcc.Dropdown(
        id='major_cat_dd',
        # Set up the Major Category options with the same label and value
        options=[{'label':____, 'value':____} for category in ____],
      style={'width':'200px', 'margin':'0 auto'}),
      html.Br(),
      html.H3('Minor Category Select'),
      dcc.Dropdown(
        id='minor_cat_dd',
        style={'width':'200px', 'margin':'0 auto'})
        ],
        style={'width':'350px', 'height':'350px', 'display':'inline-block', 
               'vertical-align':'top', 'border':'1px solid black', 'padding':'20px'}),
    html.Div(
      children=[
      dcc.Graph(id='sales_line')],
      style={'width':'700px', 'height':'650px','display':'inline-block'})
    ]),], 
  style={'text-align':'center', 'display':'inline-block', 'width':'100%'})


# Create a callback from the Major Category dropdown to the Minor Category Dropdown
@app.callback(
    Output('____', '____'),
    Input('____', '____'))

def update_minor_dd(major_cat_dd):
  
    major_minor = ecom_sales[['Major Category', 'Minor Category']].drop_duplicates()
    relevant_minor_options = major_minor[major_minor['Major Category'] == major_cat_dd]['Minor Category'].values.tolist()
    
    # Create and return formatted relevant options with the same label and value
    formatted_relevant_minor_options = [{'label':x, 'value':x} for x in ____]
    return ____

# Create a callback for the Minor Category dropdown to update the line plot
@app.callback(
    Output('____', '____'),
    Input('____', 'value'))

def update_line(minor_cat):
    minor_cat_title = 'All'
    ecom_line = ecom_sales.copy()
    
    if minor_cat:
        minor_cat_title = minor_cat
        ecom_line = ecom_line[ecom_line['Minor Category'] == minor_cat]
    
    ecom_line = ecom_line.groupby('Year-Month')['OrderValue'].agg('sum').reset_index(name='Total Sales ($)')
    line_graph = px.line(ecom_line, x='Year-Month',  y='Total Sales ($)', 
                         title=f'Total Sales by Month for Minor Category: {minor_cat_title}')
    
    return line_graph


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