Search code examples
pythonplotlyplotly-dash

Updating boxplot when choosing rows


I need to allow users to choose particular rows in a data frame and generate a boxplot.

My implementation does not allow me to update a boxplot when I choose rows. Can somebody please point out what I do wrong? I am interested in only one column which is ['Phase'].

Here is my implementation.

from dash import Dash, dash_table, dcc, html
from dash.dependencies import Input, Output
import pandas as pd
import plotly.graph_objects as px


df = summary_extended.copy()

app = JupyterDash(__name__)

app.layout = html.Div([
    dash_table.DataTable(
        id='datatable-interactivity',
        columns=[
            {"name": i, "id": i, "deletable": True, "selectable": True} for i in df.columns
        ],
        data=df.to_dict('records'),
        editable=True,
        filter_action="native",
        sort_action="native",
        sort_mode="multi",
        column_selectable="single",
        row_selectable="multi",
        row_deletable=True,
        selected_columns=[],
        selected_rows=[],
        page_action="native",
        page_current= 0,
        page_size= 10,
    ),
    html.Div(id='datatable-interactivity-container')
])

@app.callback(
    Output('datatable-interactivity-container', "children"),
    Input('datatable-interactivity', "derived_virtual_data"),
    Input('datatable-interactivity', "derived_virtual_selected_rows"))
def update_graphs(rows, derived_virtual_selected_rows):
    if derived_virtual_selected_rows is None:
        derived_virtual_selected_rows = []

    dff = summary_extended if rows is None else pd.DataFrame(rows)
    print('rows', rows)

    colors = ['#7FDBFF' if i in derived_virtual_selected_rows else '#0074D9'
              for i in range(len(dff))]
    y= dff['Phase']      
    trace0 = go.Box(y=y)
    data=[trace0]

    return [
            dcc.Graph(figure={'data': data}, id='box-plot')
    ]



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

Solution

  • I am inexperienced with Dash, but from the example in the official reference, I would guess that the reason is that the value of the active cell is not being retrieved. I found an example of updating a graph by clicking on a data table, so I used that as a basic form to create the code with appropriate sample data. The key point is that I set the initial active cell, and in the callback, I extract the data frame from the row and column data of the latest active cell, from the row index, and use that as the graph data.

    from dash import Dash, dash_table, dcc, html, no_update
    from dash.dependencies import Input, Output
    from jupyter_dash import JupyterDash
    import pandas as pd
    import plotly.graph_objects as px
    import plotly.express as px
    
    external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']
    
    #app = Dash(__name__, external_stylesheets=external_stylesheets)
    app = JupyterDash(__name__, external_stylesheets=external_stylesheets)
    
    mixed_df = px.data.experiment(indexed=False)
    mixed_df['experiment_1'] = round(mixed_df['experiment_1'], 2)
    mixed_df['experiment_2'] = round(mixed_df['experiment_2'], 2)
    mixed_df['experiment_3'] = round(mixed_df['experiment_3'], 2)
    mixed_df['experiment_4'] = round(mixed_df['experiment_1'] * 0.85, 2)
    mixed_df['experiment_5'] = round(mixed_df['experiment_2'] * 0.85, 2)
    mixed_df['experiment_6'] = round(mixed_df['experiment_3'] * 0.85, 2) 
    mixed_df['id'] = mixed_df.index
    mixed_df = mixed_df[['id','gender','group','experiment_1','experiment_2','experiment_3','experiment_4','experiment_5','experiment_6']]
    mixed_df.columns = ['id','gender','group','experi_1','experi_2','experi_3','experi_4','experi_5','experi_6']
    columns = mixed_df.columns
    initial_active_cell = {"row": 0, "column": 0, "column_id": "id", "row_id": 0}
    
    app.layout = html.Div(
        [
            html.Div(
                [
                    html.H3("Experiment Box Plot", style={"textAlign":"center"}),
                    dash_table.DataTable(
                        id="table",
                        columns=[{"name": c, "id": c} for c in columns],
                        data=mixed_df.to_dict("records"),
                        page_size=10,
                        sort_action="native",
                        active_cell=initial_active_cell,
                    ),
                ],
                style={"margin": 50},
                className="seven columns"
            ),
            html.Div(id="output-graph", className="three columns"),
        ],
        className="row"
    )
    
    
    @app.callback(
        Output("output-graph", "children"), Input("table", "active_cell"),
    )
    def cell_clicked(active_cell):
        if active_cell is None:
            return no_update
    
        row = active_cell["row_id"]
        print(f"row id: {row}")
        print("---------------------")
        
        dff = mixed_df.iloc[row,:].to_frame().T
        #print(dff)
        fig = px.box(
            dff, x='gender', y=dff.columns[2:], title=f'Selected row num: {row}'
        )
        fig.update_layout(title={"font_size": 20}, title_x=0.5, margin=dict(t=190, r=15, l=5, b=5))
    
        return dcc.Graph(figure=fig)
    
    
    if __name__ == "__main__":
        app.run_server(debug=True, mode='inline')
    

    enter image description here