Reading a, specified dataframe or specified piece of info, from dcc.Store

Greetings

I am trying to save multiple dataframes (or multiple pieces of data) to a one dcc.store component and recall any given dataframe seperately. I have recreated a scenario (below), where on initial load of the app, I add data (names of columns of some dummy dataframes) to the dcc.store, then I have two buttons to selectively pick which dataframe’s columns I want to view. How would one go about doing this?? I have seen other forum posts (like this one) of this, but I am still confused on the use of "MATCH" in the "index" of a dcc component? What is being matched, to recall the data you want? What would the callback look like for my code, to recall the the column names for d or e selectively?

from dash import Dash, dcc, html, Input, Output, State
import pandas as pd

d = pd.DataFrame({"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]})
e = pd.DataFrame({"one": [5,6,7,8,9], "three": [9,8,7,6,5]})

app = Dash(__name__)

app.layout = html.Div([
    dcc.Store(id = "something", storage_type = "local"),
    html.Button("d", id = "d", n_clicks = 0),
    html.Button("e", id = "e", n_clicks = 0),
    html.Div(id = "output_columns", children = "columns", style = {"color": "white"})
])

#Upon loading of app, info (name of columns of 'd' and 'e' dataframes) saved to dcc.store
@app.callback(
    Output('something', 'data'),
    Input('d', 'n_clicks'),
    Input('e', 'n_clicks')
)

def update_output(a, b):
    if (a == 0 and b == 0):
        return [d.columns, e.columns]

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

Hi @Suva1

The forum post you referenced is a more complex app - that example had multiple dataframes that were loaded and stored dynamically and used pattern matching callbacks.. That’s not necessary in your example.

If you load the data at the start like you show, and don’t update it later in the app, then you don’t even need to use a dcc.Store. You could simply do something like this:

from dash import Dash, dcc, html, Input, Output, State, callback_context
import pandas as pd

d = pd.DataFrame({"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]})
e = pd.DataFrame({"one": [5,6,7,8,9], "three": [9,8,7,6,5]})

app = Dash(__name__)

app.layout = html.Div([
    html.Button("d", id = "d", n_clicks = 0),
    html.Button("e", id = "e", n_clicks = 0),
    html.Div(id = "output_columns", children = "columns")
])


@app.callback(
    Output("output_columns", "children"),
    Input("d", "n_clicks"),
    Input("e", "n_clicks"),
    prevent_initial_call=True
)
def display_columns(d_button,e_button):
    ctx = callback_context
    input_id = ctx.triggered[0]['prop_id'].split('.')[0]

    return d.columns if input_id == 'd' else e.columns


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

See more about figuring out which input triggered the callback here.

If you plan to update the data in dcc.Store in your app, then you can include it in the callback like this:
State("something", "data)
If you would like a complete MWE, let me know.

1 Like

Hi @AnnMarieW, thank you for responding.

However, the example I provided in my code above, is a minimalistic example of what I am really trying to achieve. I have an app that where I am changing the content in 5 tables, when a dropdown changes, and I would like to use one dcc.Store to store the data for these tables (5 dataframes). Hence, I would like to know how to call the data selectively to place them in their correct tables?

I think the example I provided was too simple, I am sorry for that. I have attached the edited code where I am choosing to store the dataframes and not just it’s columns, from dcc.Store. In using the buttons to selectively choose to display the d or e dataframe, how would one implement this in a callback?

Thank you for your patience.

from dash import Dash, dcc, html, Input, Output, State
import pandas as pd

d = pd.DataFrame({"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]})
e = pd.DataFrame({"one": [5,6,7,8,9], "three": [9,8,7,6,5]})

app = Dash(__name__)

app.layout = html.Div([
    dcc.Store(id = "something", storage_type = "local"),
    html.Button("d", id = "d", n_clicks = 0),
    html.Button("e", id = "e", n_clicks = 0),
    html.Div(id = "output_columns", children = "columns", style = {"color": "white"})
])

#Upon loading of app, info (name of columns of 'd' and 'e' dataframes) saved to dcc.store
@app.callback(
    Output('something', 'data'),
    Input('d', 'n_clicks'),
    Input('e', 'n_clicks')
)

def update_output(a, b):
    if (a == 0 and b == 0):
        return [d, e]

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

To my knowledge, what you want to do is not possible in Dash. You can’t read/write part of a property, only the whole thing. But the solution is obvious - just use five Store components, one for each dataframe.

Hey @Suva1

While it’s true as @Emil said, you can’t read or write part of a property, it looks like you aren’t trying to do that. You can put all your dataframes in one dcc.Store by using a dictionary (assuming the data fits given the browser’s limit on size).

Depending on how you use this data in your callbacks, it might be more efficient to use one dcc.Store for each df. You mentioned that you want to use all 5 dataframes to update tables based on a dropdown. In this case, it probably makes sense to use one dcc.Store.

Here’s an example:

from dash import Dash, dcc, html, dash_table, Input, Output, State, callback_context
import pandas as pd

d = pd.DataFrame({"one": [1.0, 2.0, 3.0, 4.0], "two": [4.0, 3.0, 2.0, 1.0]})
e = pd.DataFrame({"one": [5, 6, 7, 8, 9], "three": [9, 8, 7, 6, 5]})

app = Dash(__name__)

app.layout = html.Div(
    [
        dcc.Store(id="something"),
        html.Button("d", id="d", n_clicks=0),
        html.Button("e", id="e", n_clicks=0),
        html.Div(id="output_columns"),
    ]
)


# Upon loading of app, load data into store
@app.callback(
    Output("something", "data"), Input("something", "data"),
)
def update_output(_):
    return {"d": d.to_dict("records"), "e": e.to_dict("records")}


@app.callback(
    Output("output_columns", "children"),
    Input("d", "n_clicks"),
    Input("e", "n_clicks"),
    State("something", "data"),
    prevent_initial_call=True,
)
def display_columns(d_button, e_button, store):
    ctx = callback_context
    input_id = ctx.triggered[0]["prop_id"].split(".")[0]
    df = pd.DataFrame(store[input_id])
    return html.Div(
        dash_table.DataTable(
            columns=[{"name": i, "id": i} for i in df.columns],
            data=df.to_dict("records"),
        ),
        style={"margin": 50},
    )


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

table_store

4 Likes

Hi @AnnMarieW and @Emil

Thank you for replying, this seems like exactly what I would need to do, to achieve what I want.

A small word of caution regarding the approach proposed by @AnnMarieW ; you will transfer all data (not just the dataframe you need) between client and server in each (Python) callback. If the data is very small is doesn’t matter, nor if you use clientside callbacks. However, if you have larger dataframes and use Python callbacks, performance might become an issue. That’s why I suggested using one Store component for each dataframe; then you’ll only transfer the data that you actually need :slight_smile:

4 Likes