How can callback handle variable number of multiple inputs

Hello Dash community,

I would like to develop a program which is able to dynamically compute the mean for different types of distributions (normal, lognormal). The distribution functions requires two respectively three input parameters.

My callback returns an : “A nonexistent object was used in an Input …” error since always three input parameters are passed to the function.

Does anyone have an idea how to solve this issue?

Thanks

import dash
from jupyter_dash import JupyterDash
import dash_core_components as dcc
import dash_html_components as html
import dash_bootstrap_components as dbc
from dash.dependencies import Input, Output, State
import plotly.graph_objs as go
import pandas as pd
import numpy as np
import scipy.stats as stats

#app = dash.Dash()
app = JupyterDash(external_stylesheets=[dbc.themes.BOOTSTRAP], suppress_callback_exceptions=True)

app.layout = html.Div([
dbc.Card(
    dbc.CardBody([
        dbc.Row([
            dbc.Col([
                
                dbc.Row([
                    dcc.Dropdown(
                        id='dd-dist',
                        clearable = False,
                        options=[
                            {'label': 'Normal', 'value': 'NOR'},
                            {'label': 'Log-Normal', 'value': 'LGN'},
                        ], 
                        style={'width': '60%'},
                        value='LGN'
                    )
                ],),
                
                

                html.Div(
                    id='parameter-input'
                ),
                
                dbc.Row([
                    dbc.Col([
                        html.H5('Mean')
                    ],width={'size': 5, 'offset': 0}),
                    dbc.Col([
                        dcc.Input(
                            id='mean-out',
                            value= None,
                        )
                    ]) 
                ]),
                
                
            
            ], width={'size': 4, 'offset': 0, 'order': 1}),
            
        ], align='center'),

    ])
)
    
], style={'padding': 10})



@app.callback(
    Output('parameter-input', 'children'),
    [Input('dd-dist', 'value')])
def update_parameter(value):
    if 'LGN' == value:
        return (dbc.Row([
                    dbc.Col([
                        html.H5('Shape')
                    ],width={'size': 5, 'offset': 0}),
                    dbc.Col([
                        dcc.Input(
                            id='param1-in',
                            debounce = True,
                            value = 1,
                            min= 0,
                            max= 10,
                            style = {'fontSize': 14}
                        )
                    ],width={'size': 4, 'offset': 0}, align='end')
                    
                    
                ]),
   
            dbc.Row([
                    dbc.Col([
                        html.H5('Location')
                    ],width={'size': 5, 'offset': 0}),
                    dbc.Col([
                        dcc.Input(
                            id='param2-in',
                            debounce = True,
                            value = 1,
                            min=0,
                            max=5,
                            style = {'fontSize': 14}
                        )
                    ],width={'size': 4, 'offset': 0}, align='end')
                    
                    
                ]),
            
            dbc.Row([
                    dbc.Col([
                        html.H5('Scale')
                    ],width={'size': 5, 'offset': 0}),
                    dbc.Col([
                        dcc.Input(
                            id='param3-in',
                            debounce = True,
                            value = 1,
                            min=0,
                            max=5,
                            style = {'fontSize': 14}
                        )
                    ],width={'size': 4, 'offset': 0}, align='end')
                    
                    
                ])
        )
    
    elif 'NOR' == value:
        return (dbc.Row([
                    dbc.Col([
                        html.H5('Mean')
                    ],width={'size': 5, 'offset': 0}),
                    dbc.Col([
                        dcc.Input(
                            id='param1-in',
                            debounce = True,
                            value = 1,
                            min= 0,
                            max= 10,
                            style = {'fontSize': 14}
                        )
                    ],width={'size': 4, 'offset': 0}, align='end')
                    
                    
                ]),
   
            dbc.Row([
                    dbc.Col([
                        html.H5('Standard deviation')
                    ],width={'size': 5, 'offset': 0}),
                    dbc.Col([
                        dcc.Input(
                            id='param2-in',
                            debounce = True,
                            value = 1,
                            min=0,
                            max=5,
                            style = {'fontSize': 14}
                        )
                    ],width={'size': 4, 'offset': 0}, align='end')
            ])
        )

@app.callback(
    Output('mean-out', 'value'),
    [Input('dd-dist', 'value'),
     Input('param1-in', 'value'),
     Input('param2-in', 'value'),
     Input('param3-in', 'value')
     ]
)
def return_mean(dist, param1, param2, param3):
    if dist == 'LGN':
        return round(stats.lognorm.mean(float(param1), float(param2), float(param3)), 3)
    elif dist == 'NOR':
        return round(stats.norm.mean(float(param1), float(param2)), 3)
       
if __name__ == '__main__':
    app.run_server(mode='external')

In your first callback the elif creates Input param1-in and param2-in, but no param3-in. The fourth Input object Input('param3-in', 'value') is thus missing when return_mean is getting called. Every Input object needs to exists before you can use them in a callback.

I figured that. Do you have an idea what would be a good way to circumvent that in this example?

Only way I know of is to define every component you need in the layout first and then address them in a callback