'Dash datatable with expandable/collapsable rows

Similar to qtTree, I would like to have a drill down on a column of a datatable. I guess this is better illustrated with an example. Assume we have a dataframe with three columns: Country, City, Population like:

Country    City        Population
USA        New-York    19MM
China      Shanghai    26MM
China      Beijing     20MM
USA        Los Angeles 12MM
France     Paris       11MM

Is there a way to present this data ideally in a dash-plotly datatable as follows:

Country    City        Population
+USA                   31MM
 /---->    New-York    19MM
 /---->    Los Angeles 12MM
+China                 46MM
 /---->    Shanghai    26MM
 /---->    Beijing     20MM
+France                11MM
 /---->    Paris       11MM

The grouping Country/City would be expandle (or maybe hidden/shown upon click on the row -?-). At the country level, the population would be the sum of its constituents and the City level, the population would be the one from that city.

The library dash_treeview_antd allows for treeview representation but I don't know how to include the population column for instance. Maybe there is a simpler way by doing the groupby in pandas first and then having a callback to hide/show the currentrow selection/clicked?

Edit: -

Edit2: I have been playing around with groupby in pandas and activecell in the callback.

def defineDF():
    df = pd.DataFrame({'Country': ['USA', 'China', 'China', 'USA', 'France'],
                   'City': ['New-York', 'Shanghai', 'Beijing', 'Los Angeles', 'Paris'],
                   'Population': [19, 26, 20, 12, 11],
                   'Other': [5, 3, 4, 11, 43]})
    df.sort_values(by=['Country', 'City'], inplace=True)
    return df

def baseDF():
    df = pd.DataFrame({'Country': ['USA', 'China', 'China', 'USA', 'France'],
                   'City': ['New-York', 'Shanghai', 'Beijing', 'Los Angeles', 'Paris'],
                   'Population': [19, 26, 20, 12, 11],
                   'Other': [5, 3, 4, 11, 43]})
    df.sort_values(by=['Country', 'City'], inplace=True)
    f = {'Population': 'sum', 'Other': 'sum'}
    cols = ['Country']
    return df.groupby(cols).agg(f).reset_index()

startDF = baseDF()

app.layout = html.Div([
    html.Div(html.H6("Country/City population"), style={"text-align":"center"}),
    html.Hr(),
    dash_table.DataTable(
        id='table',
        columns=[{'name': i, 'id': i} for i in startDF.columns],
        data = startDF.to_dict('records'),
        selected_rows=[],
        filter_action='native',
    )
])

@app.callback([
Output('table', 'data'),
Output('table', 'columns')
],
[
    Input('table', 'active_cell')
],
[
    State('table', 'data'),
    State('table', 'columns')
],
)
    def updateGrouping(active_cell, power_position, power_position_cols):
    if active_cell is None:
        returndf = baseDF()
    elif active_cell['column'] == 0:
        returndf = defineDF()
    else:
        returndf = baseDF()

    cols = [{'name': i, 'id': i} for i in returndf.columns]

    return [returndf.to_dict('records'), cols]

I am getting there. At start I only display the country column; it would be nice to have the column City there too but with empty values. Then once the user clicks on a country, only show the Cities for that country (and the corresponding Population/Other columns while the reste of the table is unchanged. I am not using current_df nor current_df_cols in the callback yet but I suspect they might become handy. Maybe I can filter the country column based on active cell (?)



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source