dcc.Slider + dcc.Interval: how to pass a dataframe to the global scope?

Hi guys,

I have a problem passing a local variable from update_graph_scatter function to a global scope where dcc.Slider resides.

My app:

  1. triggers update_graph_scatter() function every n seconds by querying a database selecting data for the last 1 month (WHERE datetime > current_timestamp - interval '31 day') and stores it in pandas dataframe
  2. shows the graph and its all very well

But

I would like the end-user of my app to be able to select a certain day on a slider. Showing a month of data is a bad idea.

But the problem is that the minimal and maximum day always change because I select the last 31 days. For that I need dcc.Slider to see the resulting dataframe from a local scope of a function update_graph_scatter().

app.layout = html.Div(children=[
        
    html.H1(children='title', style= {
        'textAlign' : 'center'
    }),

    html.Div([
        
        html.Div([
            dcc.Dropdown(
                id='yaxis-column',
                options=[{'label': label, 'value': value} for label, value in available_indicators.items()],
                value='defult_value'
            ),
        ],
        style={'width': '48%', 'display': 'inline-block'}),

    ]),

    dcc.Graph(
        id='example-graph',animate=True),dcc.Interval(id='graph-update',interval=1*30000),
    dcc.Slider(
        id='day-slider',
        min=df['datetime'].dt.day.min(),
        max=df['datetime'].dt.day.max(),
        value=df['datetime'].dt.day.max(),
        marks={str(day): str(day) for day in df['datetime'].dt.day.unique()},
        step=None
     )
])

@app.callback(Output('example-graph', 'figure'), 
              [Input('graph-update', 'interval'),
               Input('yaxis-column', 'value'),
               Input('day-slider' ,'value')])

def update_graph_scatter(interval, yaxis_column_name, day_slider):
    df = pd.read_sql("SELECT * FROM table WHERE datetime > current_timestamp - interval '31 day'", conn)
    df = df[df['datetime'].dt.day == day_slider]
    ... some transformations...

    return figure

The problem is that dcc.Slider doesn’t see df variable. But if I query the database from a global scope, it doesn’t refresh at all.

Is it possible to allow dcc.Slider and update_graph_scatter to communicate with each other? How can i update values in a graph and in a dcc.Slider by a single refresh?

Store the df in a dcc.Store (https://dash.plot.ly/dash-core-components/store) and include this component as an input to your callback. Thus when this values of the store changes, your call backback will be fired.

Recently, Dash/Plotly was updated to allow for multiple outputs. Create a list of outputs just like your input list.

Thank you for your answer. Can you please give a little example of how it can be done? The one from the tutorial seems too complex for my case :frowning:

Here is another option to try … using hidden Div to store results. (https://dash.plot.ly/sharing-data-between-callbacks).

However, the link previously provided has a simple example (see code under “Share data between callbacks”).
Basically, anywhere in your layout, include the following: They made the example more complex just to programmatically create the callbacks.

dcc.Store(id='memory-output')

Then, your callback could be modified as following:

@app.callback([Output('example-graph', 'figure'),
           Output('memory-output')], 
          [Input('graph-update', 'interval'),
           Input('yaxis-column', 'value'),
           Input('day-slider' ,'value')])

def update_graph_scatter(interval, yaxis_column_name, day_slider):
df = pd.read_sql("SELECT * FROM table WHERE datetime > current_timestamp - interval '31 day'", conn)
df = df[df['datetime'].dt.day == day_slider]
... some transformations...

return figure, df

But you may need to add another callback just to create the df dataframe if you also need it as an input to theupdate_graph_scatter` function, as you can’t have an component be both an input and an output in the same function.

Hope this helps!