Nonexistent object error in app with 2 graphs based on selection in dropdown

I’m building a dashboard that displays a graph for one of two distributions selected in a dropdown. When a user selects the Beta distribution, the Multivariate Normal distribution graph is of course suppressed, and vice-versa.

The dashboard works, but it gives the
‘A nonexistent object was used in an Input of a Dash callback.’ error. Not sure what the correct and elegant way to resolve the error is.

from dash import Dash, html, dcc, Input, Output, State
import dash_bootstrap_components as dbc
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from scipy import stats

# App Layout *******************************************

app = Dash(__name__, external_stylesheets=[dbc.themes.LITERA])
app.config.suppress_callback_exceptions = True

app.layout = dbc.Container(
    [
        dbc.Row
        (
            dbc.Col(
                html.H2(
                    "Visualizing Distributions",
                    className="text-center bg-primary text-white p-2",
                        ),
                    )
        ),
        dbc.Row(
            [
            dbc.Col(
                    [html.H3(
                            "Select distribution",
                                style={"textAlign": "center"},
                            ),
                    dcc.Dropdown(
                                id='select-distribution',
                                options=[
                                    {'label': 'Beta', 'value': 'beta'},
                                    {'label': 'Multivariate normal', 'value': 'multivariate_normal'}
                                ],
                                value='beta',
                            ),
                    html.Div(
                        id = 'sliders-or-inputs',
                        className="mt-4 p-4"
                    )
                    ],
                    width=4
                    ),
            dbc.Col(
                    [
                    html.Div([], id='beta_graph'),
                    html.Div([], id='mv_graph'),
                    ],
                    width=8
                    ),
            ]
            )
    ]
)

# Callbacks *******************************************

@app.callback(
    Output('sliders-or-inputs', 'children'),
    [Input('select-distribution', 'value')])
def display_sliders_or_inputs(distribution):
    if distribution == 'beta':
        return html.Div([
            dcc.Slider(
                id='alpha',
                min=0,
                max=100,
                value=0,
                className='four columns',
                tooltip={"placement": "bottom", "always_visible": True}
            ),
            dcc.Slider(
                id='beta',
                min=0,
                max=100,
                value=0,
                className='four columns',
                tooltip={"placement": "bottom", "always_visible": True}
            )
        ], className='row')
    elif distribution == 'multivariate_normal':
        return html.Div([
            dbc.InputGroup(
                    [
                        dbc.InputGroupText("1st variable mean"),
                        dbc.Input(
                            id="var1_mu",
                            placeholder="10",
                            type="number",
                            min=10,
                            value=58.8,
                        ),
                    ],
                    className="mb-3",
                ),
            dbc.InputGroup(
            [
                dbc.InputGroupText("1st variable standard deviation"),
                dbc.Input(
                    id="var1_sd",
                    placeholder="3",
                    type="number",
                    min=0.01,
                    value=7.82,
                ),
            ],
            className="mb-3",
            ),
            dbc.InputGroup(
                    [
                        dbc.InputGroupText("2nd variable mean"),
                        dbc.Input(
                            id="var2_mu",
                            placeholder="7",
                            type="number",
                            min=10,
                            value=60.7,
                        ),
                    ],
                    className="mb-3",
                ),
            dbc.InputGroup(
                    [
                        dbc.InputGroupText("2nd variable standard deviation"),
                        dbc.Input(
                            id="var2_sd",
                            placeholder="10",
                            type="number",
                            min=0.01,
                            value=7.6,
                        ),
                    ],
                    className="mb-3",
                ),
            dbc.InputGroup(
                [
                    dbc.InputGroupText("Variable correlation"),
                    dbc.Input(
                        id="corr",
                        placeholder="0.12",
                        type="number",
                        min=-1,
                        value=0.7,
                    ),
                ],
                className="mb-3",
                ),
        ], className='row')

@app.callback(
    Output("mv_graph", "children"),
    Input('select-distribution', 'value'),
    Input("var1_mu", "value"),
    Input("var1_sd", "value"),
    Input("var2_mu", "value"),
    Input("var2_sd", "value"),
    Input("corr", "value"),
)
def update_mv_normal_distr(distribution, var1_mu, var1_sd, var2_mu, var2_sd, corr):
    if distribution=='multivariate_normal':
        x1, x2 = np.mgrid[40:80:0.25, 40:80:0.25]
        means = np.array([var1_mu, var2_mu])

        cov = var1_sd * var2_sd * corr
        cov_mat = np.array([[var1_sd**2, cov], [cov, var2_sd**2]])

        z = stats.multivariate_normal(means, cov_mat).pdf(np.dstack((x1, x2)))

        fig = go.Figure(data=[go.Surface(z=z)])
        fig.update_xaxes(title_text='Testing distribution display')
        fig.update_yaxes(title_text='Probability Density')
        fig.update_layout(width=700, height=700, title="Multivariate Normal Distribution",
                        scene = dict(xaxis = dict(title = 'Var1'),
                                    yaxis = dict(title = 'Var2'),
                                    zaxis = dict(title = 'Probability density')),
                        margin=dict(l=0, r=50, b=50, t=50))

        return dcc.Graph(figure=fig)
    else:
        return None

@app.callback(
    Output("beta_graph", "children"),
    Input('select-distribution', 'value'),
    Input("alpha", "value"),
    Input("beta", "value")
    )
def update_beta_distr(distribution, a, b):
    if distribution=='beta':
        x = np.linspace(0, 1, 10000)
        beta_distr = stats.beta(a,b).pdf(x)
        fig = px.histogram(beta_distr, title="Beta distribution")

        return dcc.Graph(figure=fig)
    else:
        return None

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

I found this SO post. The solution feels more complex than necessary, but I’m new to Dash.

Also, general critiques re: app architecture as well as formatting welcome, since I’m learning.

Hello @matsuo_basho
you are getting this error because you are defining an object with a callback, the same object you are using in another callback. It’s like you what to compute x+2 but didn’t define x before.
Hope you understand the error

So indeed the solution is {"display":"none"}

So instead of updating the children with a callback you have to normally initialize the children in the app.layout and just update the style of each distribution input with {"display":"none"}

from dash import Dash, html, dcc, Input, Output, State
import dash_bootstrap_components as dbc
import numpy as np
import plotly.graph_objects as go
import plotly.express as px
from scipy import stats

input_beta = html.Div(
                    [
                        dcc.Slider(
                            id='alpha',
                            min=0,
                            max=100,
                            value=0,
                            className='four columns',
                            tooltip={"placement": "bottom", "always_visible": True}
                        ),
                        dcc.Slider(
                            id='beta',
                            min=0,
                            max=100,
                            value=0,
                            className='four columns',
                            tooltip={"placement": "bottom", "always_visible": True}
                        )
                    ],
                    className='row',
                    id= "input_beta"
                )

input_Mn = html.Div([
            dbc.InputGroup(
                    [
                        dbc.InputGroupText("1st variable mean"),
                        dbc.Input(
                            id="var1_mu",
                            placeholder="10",
                            type="number",
                            min=10,
                            value=58.8,
                        ),
                    ],
                    className="mb-3",
                ),
            dbc.InputGroup(
            [
                dbc.InputGroupText("1st variable standard deviation"),
                dbc.Input(
                    id="var1_sd",
                    placeholder="3",
                    type="number",
                    min=0.01,
                    value=7.82,
                ),
            ],
            className="mb-3",
            ),
            dbc.InputGroup(
                    [
                        dbc.InputGroupText("2nd variable mean"),
                        dbc.Input(
                            id="var2_mu",
                            placeholder="7",
                            type="number",
                            min=10,
                            value=60.7,
                        ),
                    ],
                    className="mb-3",
                ),
            dbc.InputGroup(
                    [
                        dbc.InputGroupText("2nd variable standard deviation"),
                        dbc.Input(
                            id="var2_sd",
                            placeholder="10",
                            type="number",
                            min=0.01,
                            value=7.6,
                        ),
                    ],
                    className="mb-3",
                ),
            dbc.InputGroup(
                [
                    dbc.InputGroupText("Variable correlation"),
                    dbc.Input(
                        id="corr",
                        placeholder="0.12",
                        type="number",
                        min=-1,
                        value=0.7,
                    ),
                ],
                className="mb-3",
                ),
        ], className='row',
        id="input_Mn"
        )
# App Layout *******************************************

app = Dash(__name__, external_stylesheets=[dbc.themes.LITERA])
app.config.suppress_callback_exceptions = True

app.layout = dbc.Container(
    [
        dbc.Row
        (
            dbc.Col(
                html.H2(
                    "Visualizing Distributions",
                    className="text-center bg-primary text-white p-2",
                        ),
                    )
        ),
        dbc.Row(
            [
            dbc.Col(
                    [html.H3(
                            "Select distribution",
                                style={"textAlign": "center"},
                            ),
                    dcc.Dropdown(
                                id='select-distribution',
                                options=[
                                    {'label': 'Beta', 'value': 'beta'},
                                    {'label': 'Multivariate normal', 'value': 'multivariate_normal'}
                                ],
                                value='beta',
                            ),
                    html.Div(
                        [
                            input_beta,
                            input_Mn
                        ],
                        id = 'sliders-or-inputs',
                        className="mt-4 p-4"
                    )
                    ],
                    width=4
                    ),
            dbc.Col(
                    [
                    html.Div([], id='beta_graph'),
                    html.Div([], id='mv_graph'),
                    ],
                    width=8
                    ),
            ]
            )
    ]
)

# Callbacks *******************************************

@app.callback(
    Output('input_beta', 'style'),
    Output('input_Mn', 'style'),
    Input('select-distribution', 'value'),

)
def display_sliders_or_inputs(distribution):
    if distribution == 'beta':
        return {},{'display': 'none'}
    elif distribution == 'multivariate_normal':
        return  {'display': 'none'},{}
    # else: 
    #     return  {'display': 'none'},{'display': 'none'}

@app.callback(
    Output("mv_graph", "children"),
    Input('select-distribution', 'value'),
    Input("var1_mu", "value"),
    Input("var1_sd", "value"),
    Input("var2_mu", "value"),
    Input("var2_sd", "value"),
    Input("corr", "value"),
)
def update_mv_normal_distr(distribution, var1_mu, var1_sd, var2_mu, var2_sd, corr):
    if distribution=='multivariate_normal':
        x1, x2 = np.mgrid[40:80:0.25, 40:80:0.25]
        means = np.array([var1_mu, var2_mu])

        cov = var1_sd * var2_sd * corr
        cov_mat = np.array([[var1_sd**2, cov], [cov, var2_sd**2]])

        z = stats.multivariate_normal(means, cov_mat).pdf(np.dstack((x1, x2)))

        fig = go.Figure(data=[go.Surface(z=z)])
        fig.update_xaxes(title_text='Testing distribution display')
        fig.update_yaxes(title_text='Probability Density')
        fig.update_layout(width=700, height=700, title="Multivariate Normal Distribution",
                        scene = dict(xaxis = dict(title = 'Var1'),
                                    yaxis = dict(title = 'Var2'),
                                    zaxis = dict(title = 'Probability density')),
                        margin=dict(l=0, r=50, b=50, t=50))

        return dcc.Graph(figure=fig)
    else:
        return None

@app.callback(
    Output("beta_graph", "children"),
    Input('select-distribution', 'value'),
    Input("alpha", "value"),
    Input("beta", "value")
    )
def update_beta_distr(distribution, a, b):
    if distribution=='beta':
        x = np.linspace(0, 1, 10000)
        beta_distr = stats.beta(a,b).pdf(x)
        fig = px.histogram(beta_distr, title="Beta distribution")

        return dcc.Graph(figure=fig)
    else:
        return None

if __name__ == "__main__":
    app.run_server(debug=True)
1 Like

Thanks very much for fixing the code. I’m trying to understand why my version didn’t work compared to yours, but I’m not quite there. So in my version, it doesn’t work because even though in display_sliders_or_inputs we only return the selection objects for the appropriate distribution, the callback for the other function that’s not chosen doesn’t know that the object isn’t there, and so that’s the error.

But in your version, we’re addressing the appearance of the selection objects (sliders for beta, input groups for multivariate norm) using the style functionality. However, why does the graphing function not throw the same error? As in, why does using the style component property let the callback for the function that doesn’t have inputs not to error out?

1 Like

Hello @matsuo_basho,

Your code was throwing errors because technically, on first load, your inputs needed for the second callback did not exist. You were adding them through the first callback.

@AbdelAnlah, changed your code to have the second div be hidden until you used the first callback. Then passed it a display to bring it out to the client.

Yours throws the error because at first try, the inputs don’t exist when the dash renderer is checking for them. This wouldn’t break the site because of how you structured it.

Ways to fix this are as @AbdelAnlah used, to hide the div until desired to show.

Another way to do it is to use pattern-matching. Since pattern-matching uses wildcards, you can have 0 matching elements, or 100s. It doesn’t matter to the renderer.

1 Like

Hello @matsuo_basho good questions.

with @jinnyzor explanation you have a better understanding of the error. Hope it’s helpful .

All the functions have inputs, with default values assigned to all of them. However, for the graphing function, if the if statement is not met, the function does nothing and returns nothing . As a result, the function primarily depends on the distribution , rather than on personal input . Additionally, {"display":"none"} only hides the div from the user and not from the machine.

Thanks for the explanations. Still not understanding completely why the new version doesn’t give a warning.

If we examine the attached callback diagram, we see that if the beta distribution is selected, then we send the distribution argument to both functions. However, the other parameters for the multivariate normal distribution don’t exist. And so because they don’t exist, why doesn’t Dash complain about this with a warning or error?

I understand that we’re returning none for the multivariate normal function in this situation, but I’m under the impression that the update_mv_normal_distr function, and all other functions with callbacks run automatically, don’t they?

Actually the other parameters for the multivariate normal distribution exist, but just hide

Comment # the first callback and the display_sliders_or_inputs function and run the app

1 Like

Ok, that recommendation to comment out the first callback made it clear, I understand. Thank you!