Search code examples
pythonplotlyplotly-dashplotly-python

Plotly Treemap using Dash


I am trying to replicate Treemap using dash/dashboard.

import plotly.express as px
df = px.data.tips()
fig = px.treemap(df, path=[px.Constant("all"), 'sex', 'day', 'time'], 
                 values='total_bill', color='day')
fig.update_layout(margin = dict(t=50, l=25, r=25, b=25))
fig.show()

#data

 total_bill tip     sex  smoker day time    size
0   16.99   1.01    Female  No  Sun Dinner  2
1   10.34   1.66    Male    No  Sun Dinner  3
2   21.01   3.50    Male    No  Sun Dinner  3
3   23.68   3.31    Male    No  Sun Dinner  2
4   24.59   3.61    Female  No  Sun Dinner  4
... ... ... ... ... ... ... ...
239 29.03   5.92    Male    No  Sat Dinner  3
240 27.18   2.00    Female  Yes Sat Dinner  2
241 22.67   2.00    Male    Yes Sat Dinner  2
242 17.82   1.75    Male    No  Sat Dinner  2
243 18.78   3.00    Female  No  ThurDinner  2

using fig.show() i can generate treemap easily, but I would like to know how to replicate this using dash with dropdown for smoker and nonsmoker.

  1. I want to implement how we can add a dropdown so that i can only look at the treemap for smoker or non-smoker based on Yes or No
  2. how to manipulate hoverdata? if I hover over one of the boxes, it shows only total_sales for that box. ie) I want something that shows average of total_sales, and minimum sales when i hover over one of the boxes..

Any help/guidance how to do this with JUPYTER_DASH/DASH would be really appreciated..

Thank you!

Attempt #1:

import pandas as pd
from dash import html, dcc, Input, Output
from dash.exceptions import PreventUpdate
from jupyter_dash import JupyterDash
import plotly
import dash
import plotly.express as px
import warnings
warnings.filterwarnings('ignore')

def treemap(df, metric='total_bill', path=[px.Constant("all"), 'sex', 'day', 'time']):

    fig = px.treemap(df, path=path, template='none', values=metric, height=650)
    template = '<b>%{label}</b><br><br>Total: %{value:,d}<br>%{percentParent:.1%}'
    fig.data[0]['texttemplate'] = template
    fig.data[0]['hovertemplate'] = template
    return fig
df = px.data.tips()

app = JupyterDash(__name__)

app.layout = html.Div([
    html.Br(), html.Br(),
    dbc.Row([
        dbc.Col(lg=1),
        dbc.Col([
            dbc.Label('Path:'),
        dcc.Dropdown(id='path',
                     options=[{'label': 'Smoker', 'value': 'Yes'},
                              {'label': 'NonSmoker', 'value': 'No'}],
                     value='smoker'),
        ]),

        dbc.Col(), html.Br(), html.Br(),
        dcc.Graph(id='graph')
    ])
])


@app.callback(Output('graph', 'figure'), Input('path', 'value'))
def make_treemap(path):     
    
    dff = df.query('smoker == @path')     
    fig = treemap(dff)#, metric=metric, path=path.split()     
    return fig

app.run_server(debug=False, height=1000, mode='inline')

I could not replicate it :(


Solution

  • I don't have enough time right now to verify the numbers, but I have added three dropdown choices: smoking and non-smoking overall, smoking and non-smoking, plus three numerical indicators that I thought you might have wanted to add. A second dropdown has been added. As for the callback function, if the entire smoker is selected, it will copy and pass the entire data frame without extracting. Otherwise, it will extract by the result of the dropdown selection. The indicators also pass the selected values to the graph function.

    import pandas as pd
    from dash import html, dcc, Input, Output
    from dash.exceptions import PreventUpdate
    from jupyter_dash import JupyterDash
    import dash_bootstrap_components as dbc
    import plotly
    import dash
    import plotly.express as px
    import warnings
    warnings.filterwarnings('ignore')
    
    def treemap(df, metric='total_bill', path=[px.Constant("all"), 'sex', 'day', 'time']):
    
        fig = px.treemap(df, path=path, template='none', values=metric, height=650)
        template = '<b>%{label}</b><br><br>Total: %{value:,d}<br>%{percentParent:.1%}'
        fig.data[0]['texttemplate'] = template
        fig.data[0]['hovertemplate'] = template
        return fig
    
    df = px.data.tips()
    
    app = JupyterDash(__name__)
    
    app.layout = html.Div([
        html.Br(), html.Br(),
        dbc.Row([
            dbc.Col(lg=1),
            dbc.Col([
                dbc.Label('Path:'),
            dcc.Dropdown(id='path',
                         options=[{'label': 'All', 'value': 'Yes and No'},
                                  {'label': 'Smoker', 'value': 'Yes'},
                                  {'label': 'NonSmoker', 'value': 'No'}],
                         value='Yes and No'),
            ]),
            dbc.Col([
                dbc.Label('Metric:'),
                dcc.Dropdown(id='metric',
                             options=[{'label': m, 'value': m} for m in ['total_bill', 'tip', 'size']],
                             value='total_bill')
            ]),
            dbc.Col(), html.Br(), html.Br(),
            dcc.Graph(id='graph')
        ])
    ])
    
    
    @app.callback(
        Output('graph', 'figure'),
        Input('path', 'value'),
        Input('metric', 'value')
    )
    def make_treemap(path, metric):
        if not path or not metric:
            raise PreventUpdate
        if path == 'Yes and No':
            dff = df.copy()
            #print(dff)
        else:
            dff = df.query('smoker == @path')
        fig = treemap(dff, metric)
        return fig
    
    app.run_server(debug=True, height=1000, mode='inline')
    

    enter image description here