Skip to content Skip to sidebar Skip to footer

Properly Setting Up Callbacks For Dynamic Dropdowns Plotly Dash

I am trying to create a Dash dashboard where dropdown options in one box are dependent on the previous dropdown selection. The data consists of two dictionaries, with two keys each

Solution 1:

I haven't been able to figure out why your code fails. But I've been putting together an example that I think will come close to what you're looking for here. It builds on an example from the plotly docs, and so has a bit different layout from what you've got in your question. The main take-away is that three sets of radio buttons will let you:

  1. select a weight: ['prod', 'area'],
  2. which in turn will define the options in another callback: ['2m_temp_prod', 'total_precip_prod'] or ['2m_temp_area', 'total_precip_area'].
  3. And you're also able to select produce ['corn', 'soybeans']

It's very possible that I've misunderstood the logic of what you want to achieve here. But just give me some feedback along the way and we can work out the details.

Dash app for selection DF: prod | Crops: corn | Column: 2m_temp_prod

enter image description here

Dash app for selection DF: area | Crops: soybeans | Column: total_precip_area

enter image description here

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

# data
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
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
from pandas import Timestamp
import numpy as np

# data ##########################################################################
index1= [1,2,3,4]
columns1 =['time', '2m_temp_prod' , 'total_precip_prod']

index2= [1,2,3,4]
columns2 = ['time', '2m_temp_area', 'total_precip_area']

df_vals_prod = {'corn': pd.DataFrame(index=index1, columns = columns1,
                                data= np.random.randn(len(index1),len(columns1))).cumsum(),
                'soybeans' : pd.DataFrame(index=index1, columns = columns1,
                                     data= np.random.randn(len(index1),len(columns1))).cumsum()}

df_vals_area= {'corn': pd.DataFrame(index=index2, columns = columns2,
                                data= np.random.randn(len(index2),len(columns2))).cumsum(),
               'soybeans' : pd.DataFrame(index=index2, columns = columns2,
                                     data= np.random.randn(len(index2),len(columns2))).cumsum()}

# mimic data properties of your real world data
df_vals_prod['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'), 
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['corn'].set_index('time', inplace = True)
df_vals_prod['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['soybeans'].set_index('time', inplace = True)

df_vals_area['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['corn'].set_index('time', inplace = True)
df_vals_area['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['soybeans'].set_index('time', inplace = True)

# dash ##########################################################################
app = JupyterDash(__name__)

# weighting
all_options = {
    'prod': list(df_vals_prod[list(df_vals_prod.keys())[0]].columns),
    'area': list(df_vals_area[list(df_vals_prod.keys())[0]].columns)
}

app.layout = html.Div([
    dcc.RadioItems(
        id='produce-radio',
        options=[{'label': k, 'value': k} for k in all_options.keys()],
        value='prod'
    ),

    html.Hr(),
    
    dcc.RadioItems(
        id='crop-radio',
        options=[{'label': k, 'value': k} for k in list(df_vals_prod.keys())],
        value=list(df_vals_prod.keys())[0]
    ),

    html.Hr(),

    dcc.RadioItems(id='columns-radio'),

    html.Hr(),

    html.Div(id='display-selected-values'),
    
    dcc.Graph(id="crop-graph")
])

# Callbacks #####################################################################

# Weighting selection.
@app.callback( # Dataframe PROD or AREA
    Output('columns-radio', 'options'),
    # layout element: dcc.RadioItems(id='produce-radio'...)
    [Input('produce-radio', 'value')])
def set_columns_options(selected_produce):
    varz =  [{'label': i, 'value': i} for i in all_options[selected_produce]]
    print('cb1 output: ')
    print(varz)
    return [{'label': i, 'value': i} for i in all_options[selected_produce]]

# Columns selection
@app.callback( 
    Output('columns-radio', 'value'),
    # layout element: dcc.RadioItems(id='columns-radio'...)
    [Input('columns-radio', 'options')])
def set_columns(available_options):
    return available_options[0]['value']

# Crop selection
@app.callback( 
    Output('crop-radio', 'value'),
    # layout element: dcc.RadioItems(id='columns-radio'...)
    [Input('crop-radio', 'options')])
def set_crops(available_crops):
    return available_crops[0]['value']

# Display selections in its own div
@app.callback( # Columns 2m_temp_prod, or....
    Output('display-selected-values', 'children'),
    [Input('produce-radio', 'value'),
     Input('crop-radio', 'value'),
     Input('columns-radio', 'value')])
def set_display_children(selected_produce, available_crops, selected_column):
    return('DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ selected_column)

# Make a figure based on the selections
@app.callback( # Columns 2m_temp_prod, or....
    Output('crop-graph', 'figure'),
    [Input('produce-radio', 'value'),
     Input('crop-radio', 'value'),
     Input('columns-radio', 'value')])
def make_graph(selected_produce, available_crops, selected_column):
    
    # data source / weighting
    if selected_produce == 'prod':
        dfd = df_vals_prod
    if selected_produce == 'area':
        dfd = df_vals_area
    
    # plotly figure
    fig = go.Figure()
    fig.add_trace(go.Scatter(x=dfd[available_crops].index, y=dfd[available_crops][selected_column]))
    fig.update_layout(title=dict(text='DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ selected_column))
    return(fig)

app.run_server(mode='inline', port = 8077, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True)

Edit 1 - Dropdown menus.

All you have to do to get the desired dropdown buttons is to change each

dcc.RadioItems()

to

 dcc.Dropdown()

Now you'll get:

enter image description here

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

# data
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
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
from pandas import Timestamp
import numpy as np

# data ##########################################################################
index1= [1,2,3,4]
columns1 =['time', '2m_temp_prod' , 'total_precip_prod']

index2= [1,2,3,4]
columns2 = ['time', '2m_temp_area', 'total_precip_area']

df_vals_prod = {'corn': pd.DataFrame(index=index1, columns = columns1,
                                data= np.random.randn(len(index1),len(columns1))).cumsum(),
                'soybeans' : pd.DataFrame(index=index1, columns = columns1,
                                     data= np.random.randn(len(index1),len(columns1))).cumsum()}

df_vals_area= {'corn': pd.DataFrame(index=index2, columns = columns2,
                                data= np.random.randn(len(index2),len(columns2))).cumsum(),
               'soybeans' : pd.DataFrame(index=index2, columns = columns2,
                                     data= np.random.randn(len(index2),len(columns2))).cumsum()}

# mimic data properties of your real world data
df_vals_prod['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'), 
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['corn'].set_index('time', inplace = True)
df_vals_prod['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_prod['soybeans'].set_index('time', inplace = True)

df_vals_area['corn']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                  Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['corn'].set_index('time', inplace = True)
df_vals_area['soybeans']['time'] =   [Timestamp('2020-09-23 06:00:00'), Timestamp('2020-09-23 12:00:00'),
                                      Timestamp('2020-09-23 18:00:00'), Timestamp('2020-09-24 00:00:00')]
df_vals_area['soybeans'].set_index('time', inplace = True)

# dash ##########################################################################
app = JupyterDash(__name__)

# weighting
all_options = {
    'prod': list(df_vals_prod[list(df_vals_prod.keys())[0]].columns),
    'area': list(df_vals_area[list(df_vals_prod.keys())[0]].columns)
}

app.layout = html.Div([
    dcc.Dropdown(
        id='produce-radio',
        options=[{'label': k, 'value': k} for k in all_options.keys()],
        value='area'
    ),
#     dcc.Dropdown(
#     id='produce-radio',
#     options=[
#         {'label': k, 'value': k} for k in all_options.keys()
#     ],
#     value='prod',
#     clearable=False),
    

    html.Hr(),
    
    dcc.Dropdown(
        id='crop-radio',
        options=[{'label': k, 'value': k} for k in list(df_vals_prod.keys())],
        value=list(df_vals_prod.keys())[0]
    ),

    html.Hr(),

    dcc.Dropdown(id='columns-radio'),

    html.Hr(),

    html.Div(id='display-selected-values'),
    
    dcc.Graph(id="crop-graph")
])

# Callbacks #####################################################################

# Weighting selection.
@app.callback( # Dataframe PROD or AREA
    Output('columns-radio', 'options'),
    # layout element: dcc.RadioItems(id='produce-radio'...)
    [Input('produce-radio', 'value')])
def set_columns_options(selected_produce):
    varz =  [{'label': i, 'value': i} for i in all_options[selected_produce]]
    print('cb1 output: ')
    print(varz)
    return [{'label': i, 'value': i} for i in all_options[selected_produce]]

# Columns selection
@app.callback( 
    Output('columns-radio', 'value'),
    # layout element: dcc.RadioItems(id='columns-radio'...)
    [Input('columns-radio', 'options')])
def set_columns(available_options):
    return available_options[0]['value']

# Crop selection
@app.callback( 
    Output('crop-radio', 'value'),
    # layout element: dcc.RadioItems(id='columns-radio'...)
    [Input('crop-radio', 'options')])
def set_crops(available_crops):
    return available_crops[0]['value']

# Display selections in its own div
@app.callback( # Columns 2m_temp_prod, or....
    Output('display-selected-values', 'children'),
    [Input('produce-radio', 'value'),
     Input('crop-radio', 'value'),
     Input('columns-radio', 'value')])
def set_display_children(selected_produce, available_crops, selected_column):
    return('DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ selected_column)

# Make a figure based on the selections
@app.callback( # Columns 2m_temp_prod, or....
    Output('crop-graph', 'figure'),
    [Input('produce-radio', 'value'),
     Input('crop-radio', 'value'),
     Input('columns-radio', 'value')])
def make_graph(selected_produce, available_crops, selected_column):
    
    # data source / weighting
    if selected_produce == 'prod':
        dfd = df_vals_prod
    if selected_produce == 'area':
        dfd = df_vals_area
    
    # plotly figure
    fig = go.Figure()
    fig.add_trace(go.Scatter(x=dfd[available_crops].index, y=dfd[available_crops][selected_column]))
    fig.update_layout(title=dict(text='DF: ' + selected_produce +' | Crops: ' + available_crops + ' | Column: '+ selected_column))
    return(fig)

app.run_server(mode='inline', port = 8077, dev_tools_ui=True,
          dev_tools_hot_reload =True, threaded=True)

Post a Comment for "Properly Setting Up Callbacks For Dynamic Dropdowns Plotly Dash"