Can i acess selected_rows.data from another way than using row index?

I’ve been struggling with to create an filter that let me have selected_rows displayed in another table. I’m using this code

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from dash.dependencies import Input, Output, State
from dash_table import DataTable

#Define sample dataframes with the same columns
df1 = pd.DataFrame({'Category': ['A', 'B', 'C'], 'Value': [1, 2, 3], 'tipo': ['Direita','Esquerda','Direita']})
df2 = pd.DataFrame({'Category': ['D', 'E', 'F'], 'Value': [5, 6, 7], 'tipo': ['Esquerda','Direita','Esquerda']})


lista_filtros = df1['tipo'].unique()

# Define Dash app
app = dash.Dash(__name__)
app.config.suppress_callback_exceptions = True

# Define dropdown options
dropdown_options = [{'label': 'Dataframe 1', 'value': 'df1'},
                    {'label': 'Dataframe 2', 'value': 'df2'}]

# Define layout
app.layout = html.Div([
    dcc.Dropdown(
        id='dropdown',
        options=dropdown_options,
        value='df1'
    ),
    dcc.Checklist(
        id='type-checklist',
        options=[{'label': i, 'value': i} for i in lista_filtros] + [{'label': 'All', 'value': 'All'}],
        value=['All']
    ),
    html.Br(),
    DataTable(
        id='table',
        row_selectable='multi',
        selected_rows=[],
        page_size=15
    ),
    html.Button('Submit', id='submit-button', n_clicks=0),
    dcc.Store(id='selected-rows', storage_type='session'),
    html.Br(),
    html.Div([
        DataTable(
            id='selected-data',
            columns=[{'name': 'Category', 'id': 'Category'}, {'name': 'Value', 'id': 'Value'}],
            page_size=5,
            row_deletable= True,
        )
    ], id='selected-data-container', style={'display': 'none'})
])

# Define callback to update table based on dropdown selection and type checklist
@app.callback(
    Output('table', 'data'),
    [Input('dropdown', 'value'), Input('type-checklist', 'value')]
)
def update_table(selected_value, selected_type):
    if selected_value == 'df1':
        df = df1
    elif selected_value == 'df2':
        df = df2
    else:
        return []

    all_selected = 'All' in selected_type
    if all_selected:
        return df.to_dict('records')
    else:
        filtered_df = df[df['tipo'].isin(selected_type)]
        # preserve original order of rows
        filtered_df = filtered_df.set_index('tipo')
        filtered_df = filtered_df.loc[selected_type]
        filtered_df = filtered_df.reset_index()
        return filtered_df.to_dict('records')


# Define callback to store selected rows in dcc.Store
@app.callback(
    Output('selected-rows', 'data'),
    [Input('submit-button', 'n_clicks')],
    [State('table', 'selected_rows')]
)
def store_selected_rows(n_clicks, selected_rows):
    if selected_rows:
        return selected_rows

# Define callback to update selected data table visibility and content
@app.callback(
    [Output('selected-data-container', 'style'),
     Output('selected-data', 'columns'),
     Output('selected-data', 'data')],
    [Input('selected-rows', 'data')],
    [State('selected-data', 'data'),
     State('dropdown', 'value')]
)
def update_selected_data(selected_rows, stored_data, selected_value):
    if selected_rows:
        # get selected rows from selected dataframe
        if selected_value == 'df1':
            selected_df = df1.iloc[selected_rows]
        elif selected_value == 'df2':
            selected_df = df2.iloc[selected_rows]
        else:
            selected_df = pd.DataFrame()
        # add new selected data to stored data
        if stored_data is not None:
            stored_df = pd.DataFrame(stored_data)
            stored_df = stored_df.append(selected_df, ignore_index=True)
            stored_data = stored_df.to_dict('records')
        else:
            stored_data = selected_df.to_dict('records')
        # get columns for selected data table
        selected_columns = [{'name': i, 'id': i} for i in selected_df.columns]
        # return updated values
        return {'display': 'block'}, selected_columns, stored_data
    else:
        # return default values
        return {'display': 'none'}, [], None

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

The dashboard works well until I select a parameter other than “all” for my “selected_type” checklist.

This is because I am having issues with the index that the “selected_rows.data” parameter returns to me.

For example, if I use the filter “right”, what I will see is the “category = B”, which is index 0 of my “id = table” after being filtered.

However, when I press the “submit” button, my function uses index 0 in the original table before it was filtered, so I receive the data “Category = A”.

This is more of a Python problem than a Dash problem, but the use of callbacks has made it difficult for me to solve, resulting in various errors when I try to recreate a new filtered dataframe within the “updated_selected_data” function.

I just need my table to reflect exactly the data I am seeing in the previous table, and this can probably be done quite easily using any parameter other than the index. Any ideas on how I can solve this problem?

I think you don’t need to use dcc.Store to return selected_rows and you should change from:

html.Div([
        DataTable(
            id='selected-data',
            columns=[{'name': 'Category', 'id': 'Category'}, {'name': 'Value', 'id': 'Value'}],
            page_size=5,
            row_deletable= True,
        )
    ], id='selected-data-container', style={'display': 'none'})
])

to:
html.Div(id='selected-data-container')

and then return new DataTable to this DIv.

Fullcode:

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from dash.dependencies import Input, Output, State
from dash_table import DataTable

#Define sample dataframes with the same columns
df1 = pd.DataFrame({'Category': ['A', 'B', 'C'], 'Value': [1, 2, 3], 'tipo': ['Direita','Esquerda','Direita']})
df2 = pd.DataFrame({'Category': ['D', 'E', 'F'], 'Value': [5, 6, 7], 'tipo': ['Esquerda','Direita','Esquerda']})


lista_filtros = df1['tipo'].unique()

# Define Dash app
app = dash.Dash(__name__)
app.config.suppress_callback_exceptions = True

# Define dropdown options
dropdown_options = [{'label': 'Dataframe 1', 'value': 'df1'},
                    {'label': 'Dataframe 2', 'value': 'df2'}]

# Define layout
app.layout = html.Div([
    dcc.Dropdown(
        id='dropdown',
        options=dropdown_options,
        value='df1'
    ),
    dcc.Checklist(
        id='type-checklist',
        options=[{'label': i, 'value': i} for i in lista_filtros] + [{'label': 'All', 'value': 'All'}],
        value=['All']
    ),
    html.Br(),
    DataTable(
        id='table',
        row_selectable='multi',
        selected_rows=[],
        page_size=15
    ),
    html.Button('Submit', id='submit-button', n_clicks=0),
    dcc.Store(id='selected-rows', storage_type='session'),
    html.Br(),
    html.Div(id='selected-data-container')
])

# Define callback to update table based on dropdown selection and type checklist
@app.callback(
    Output('table', 'data'),
    [Input('dropdown', 'value'), 
     Input('type-checklist', 'value')]
)
def update_table(selected_value, selected_type):
    if selected_value == 'df1':
        df = df1
    elif selected_value == 'df2':
        df = df2
    else:
        return []

    all_selected = 'All' in selected_type
    if all_selected:
        return df.to_dict('records')
    else:
        filtered_df = df[df['tipo'].isin(selected_type)]
        # preserve original order of rows
        filtered_df = filtered_df.set_index('tipo')
        filtered_df = filtered_df.loc[selected_type]
        filtered_df = filtered_df.reset_index()
        return filtered_df.to_dict('records')
@app.callback(
    Output('selected-data-container', 'children'),
    [Input('submit-button', "n_clicks"),
     State('dropdown', 'value'),
     State('table', 'selected_rows')]
)
def update_bar(n_clicks, selected_value, slctd_rows):
    if selected_value == 'df1':
        df = df1
        df_filterd = df1[df1.index.isin(slctd_rows)]
    elif selected_value == 'df2':
        df = df2
        df_filterd = df2[df2.index.isin(slctd_rows)]
    return html.Div([
        DataTable(
        id='table2',
        data=df_filterd.to_dict('records')
    ),
    ])
    
# Run the app
if __name__ == '__main__':
    app.run_server(debug=False)



Please check if it was what you need.

I’m still experiencing the main error. When my checklist is marked as “all”, everything works fine, but if I want to filter by “left”, the code will take the index from my filtered table and return the data from the original table with that filtered index. In the image’s case, the category I selected is “B”, and as it is the first observation in this table, its index is zero. But when I input it, it returns “A”, which is the data with index zero in the original table.

And now I have another error, which is not being able to send data from dataframe1 and dataframe2 to view them in my selected table at the same time, that’s why I believe “dcc.Store” is useful. If I change the dataframe and try to send another input, my previous data will be changed to the data in the corresponding position of df2, rendering my application useless.

Hm I know that we should not use global in Dash but I think in this case it may help:

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
from dash.dependencies import Input, Output, State
from dash_table import DataTable

#Define sample dataframes with the same columns
df1 = pd.DataFrame({'Category': ['A', 'B', 'C'], 'Value': [1, 2, 3], 'tipo': ['Direita','Esquerda','Direita']})
df2 = pd.DataFrame({'Category': ['D', 'E', 'F'], 'Value': [5, 6, 7], 'tipo': ['Esquerda','Direita','Esquerda']})


lista_filtros = df1['tipo'].unique()

# Define Dash app
app = dash.Dash(__name__)
app.config.suppress_callback_exceptions = True

# Define dropdown options
dropdown_options = [{'label': 'Dataframe 1', 'value': 'df1'},
                    {'label': 'Dataframe 2', 'value': 'df2'}]

# Define layout
app.layout = html.Div([
    dcc.Dropdown(
        id='dropdown',
        options=dropdown_options,
        value='df1'
    ),
    dcc.Checklist(
        id='type-checklist',
        options=[{'label': i, 'value': i} for i in lista_filtros] + [{'label': 'All', 'value': 'All'}],
        value=['All']
    ),
    html.Br(),
    DataTable(
        id='table',
        row_selectable='multi',
        selected_rows=[],
        page_size=15
    ),
    html.Button('Submit', id='submit-button', n_clicks=0),
    dcc.Store(id='selected-rows'),
    html.Br(),
    html.Div(id='selected-data-container')
])

# Define callback to update table based on dropdown selection and type checklist
@app.callback(
    Output('table', 'data'),
    Output('selected-rows', 'data'),
    [Input('dropdown', 'value'), 
     Input('type-checklist', 'value')])

def update_table(selected_value, selected_type):
    global filtered_df
    if selected_value == 'df1':
        df = df1
        all_selected = 'All' in selected_type
        if all_selected:
            filtered_df = df.copy()
            return filtered_df.to_dict('records'), df.to_dict('records') 
        else:
            filtered_df = df[df['tipo'].isin(selected_type)]
            # preserve original order of rows
            filtered_df = filtered_df.set_index('tipo')
            filtered_df = filtered_df.loc[selected_type]
            filtered_df = filtered_df.reset_index()
            return filtered_df.to_dict('records'), filtered_df.to_dict('records')
        
    elif selected_value == 'df2':
        df = df2
        all_selected = 'All' in selected_type
        if all_selected:
            filtered_df = df.copy()
            return filtered_df.to_dict('records'), df.to_dict('records') 
        else:
            filtered_df = df[df['tipo'].isin(selected_type)]
            # preserve original order of rows
            filtered_df = filtered_df.set_index('tipo')
            filtered_df = filtered_df.loc[selected_type]
            filtered_df = filtered_df.reset_index()
            return filtered_df.to_dict('records'), filtered_df.to_dict('records')

@app.callback(
    Output('selected-data-container', 'children'),
    [Input('submit-button', "n_clicks"),
     State('selected-rows','value'),
     State('table', 'selected_rows')]
)
def update_bar(n_clicks, selected_value, slctd_rows):
    df_filterd = filtered_df[filtered_df.index.isin(slctd_rows)]
    
    return html.Div([
        DataTable(
        id='table2',
        data=df_filterd.to_dict('records')
    )
    ])
    
# Run the app
if __name__ == '__main__':
    app.run_server(debug=False)

Please check if it was what you need.


1 Like

Hello @Begginer.py,

Have you looked into Dash AG Grid?

You can very easily access the selected rows and display them into another grid. :slight_smile:

import dash

import dash_ag_grid as dag
from dash import Dash, html, dcc, Input, Output
import requests

app = Dash(__name__)


data = requests.get(
    r"https://www.ag-grid.com/example-assets/olympic-winners.json"
).json()


columnDefs = [
    {"field": "athlete", "checkboxSelection": True, "headerCheckboxSelection": True},
    {"field": "age"},
    {"field": "country"},
    {"field": "year"},
    {"field": "date"},
    {"field": "sport"},
    {"field": "gold"},
    {"field": "silver"},
    {"field": "bronze"},
    {"field": "total"},
]

app.layout = html.Div(
    [
        dcc.Markdown(
            "This grid has multi-select rows.  Use shift click or ctr click to select rows"
        ),
        html.Div(id="selections-multiple-output"),
        dag.AgGrid(
            id="selection-multiple-grid",
            columnDefs=columnDefs,
            rowData=data,
            columnSize="sizeToFit",
            defaultColDef={"resizable": True, "sortable": True, "filter": True},
            dashGridOptions={"rowSelection":"multiple"},
        ),
        dag.AgGrid(
            id='selected_rows',
            columnDefs=columnDefs,
            rowData=[],
            columnSize="sizeToFit",
            defaultColDef={"resizable": True, "sortable": True, "filter": True},
            dashGridOptions={"rowSelection":"multiple"},
        )
    ],
    style={"margin": 20},
)

@app.callback(
    Output("selected_rows", "rowData"),
    Input("selection-multiple-grid", "selectedRows"),
)
def selected(selected):
    if selected:
        return selected
    return []


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

As a bonus, you can apply filters and deselect, select all filtered easily as well.

3 Likes

it didn’t worked the way that i expect, but it give me enough insights to pass this.

I’ll give a look into this new way to see tables. Thank you