Black Lives Matter. Please consider donating to Black Girls Code today.

Persistent traces or graphs

I am experimenting with dash for an optimization tool for engineers. I had some minor experience with plotly and python prior to this so I thought I would try out the framework.

The tool right now is in a user manual input mode that allows users to input all the parameters and see the output.

FYI the figure updates on the click of a button where the button then takes the state of all the inputs.

I’d like to allow users to compare their current graph to their previous attempt.

I’ve hacked together a demo making traces a global variable which gets appended every time but this is obviously not sustainable for multiple users.

Also I’d rather not run the “heavy” or long duration calculation multiple times per graph by storing the input values as an intermediary.

When looking at this article: https://dash.plot.ly/sharing-data-between-callbacks

It seems like my best bet is using a cached variable. Could someone provide a guide for this for the traces = [] variable?

Thank you

Solution:

So I ended up working on a solution to this that is largely in the same vein as using a json like in: https://dash.plot.ly/sharing-data-between-callbacks

This solution only works for up to n graphs as defined by the programmer. Also I need to check and see what happens after n graphs. For now you should set n sufficiently high so that there isn’t an issue for the user.

took me awhile and I will expose my programming in-experience:

N = 100
    @app.callback(
    dash.dependencies.Output('intermediate-value', 'children'),
    [dash.dependencies.Input('manualRun', 'n_clicks')],
     state = [dash.dependencies.State('intermediate-value', 'children')]
    )
    def update_hidden(prev, etc...):
       traces = []
       if prev is not None:
            df = pd.read_json(prev, orient='split')
            traces.append(go.Scatter(x=df['x'], y=df['y']))
            for x in range(0, N):
                 tempname = 'x%s' % x
                 tempname2 = 'y%s' % x
                 if tempname in df: traces.append(go.Scatter(x=df[tempname], y=df[tempname2]))
   
   xaxis, yaxis = big_function()
   traces.append(go.Scatter(x=xaxis, y=axis))

You must then continue by the same logic to repackage the content as a json (within the same update_hidden function):

if prev is None and failure != 1:
    df = pd.DataFrame(traces[0])
    return df.to_json(date_format='iso', orient='split')
else:
    df = pd.DataFrame(traces[0])
    for x in range(1, len(traces)):
        df['x%s'%x] = traces[x]['x']
        df['y%s'%x] = traces[x]['y']

    return df.to_json(date_format='iso', orient='split')

and re-extract for the graph:

@app.callback(
    dash.dependencies.Output('graph', 'figure'),
    [dash.dependencies.Input('intermediate-value', 'children')]

)
def update_graph(cleaned_traces):
    traces = []
    dff =pd.read_json(cleaned_traces, orient='split')
    traces.append(go.Scatter(x=dff['x'], y=dff['y']))
    for x in range(1, N):
        tempname = 'x%s'%x
        tempname2 = 'y%s'%x
        if tempname in dff: traces.append(go.Scatter(x=dff[tempname], y=dff[tempname2]))

Hopes this helps someone!

1 Like

@webatdev I also try to build an optimization tool. I am curious, how did you output log to users? Its normal for an opt problem to take several minutes to solve it. If I run in console, I get some solving message (like gap). Did you encounter similar problem, and how did you solve it?