Question on customizing hover template for box plots

Dear fellow learners,

For my own learning, I experimented with customizing the hover information. It was pretty straightforward for scatter plots but I was unable to achieve what I wanted in my box plot. Currently, the hover display includes the trace name and the stat value, e.g. (Europe, q3: $33,860).

I want to have it display Q3: $33,860 without the parentheses and the trace name (“Europe”). In addition, I also failed to remove or hide the secondary box (the red “Europe”) even though I used <extra></extra> in the hovertemplate as suggested in the documentation.

I checked the documentation and online forums and tried many tips but to no avail. Hopefully, someone here can enlighten me. My code is pasted below. Thank you.

from dash import Dash, html, dcc, Input, Output, callback
import dash_ag_grid as dag
import plotly.express as px
import pandas as pd

df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/gapminder2007.csv')

columnDefs = [
    { 'field': 'country' },
    { 'field': 'pop' },
    { 'field': 'continent' },
    { 'field': 'lifeExp' },
    { 'field': 'gdpPercap' }
]

cat_vars = ['country', 'continent']

options_dict = {
    'pop':'Population',
    'lifeExp':'Life Expectancy',
    'continent':'Continent'
}

style={'width': '80%', 'margin': 'auto'}

grid = dag.AgGrid(
    id='tabular-data',
    rowData=df.to_dict('records'),
    columnDefs=columnDefs,
    style=style
)

# Initialize the app
app = Dash(__name__)

# App layout
app.layout = html.Div([
    html.H1(
        children='Population, Life Expectancy and Per Capita GDP by Country/Continent',
        style=style
    ),
    html.Hr(),
    dcc.RadioItems(
        options=options_dict,
        value='lifeExp',
        inline=True,
        style=style,
        id='radio-buton'
    ),
    html.P(),
    grid,
    dcc.Graph(
        figure={},
        style=style,
        id='my-scatter'
    )
])

# Add controls to build the interaction
@callback(
    Output(component_id='my-scatter', component_property='figure'),
    Input(component_id='radio-buton', component_property='value')
)
def update_graph(yaxis_chosen):
    hover_template = None
    if yaxis_chosen in cat_vars:
        fig = px.box(
            df,
            x=yaxis_chosen,
            y='gdpPercap',
            color='continent',
            custom_data=['country', 'continent'],
            labels={
                'gdpPercap': 'Per Capita GDP',
                yaxis_chosen: options_dict.get(yaxis_chosen, yaxis_chosen)
            }
        )
        
        fig.update_layout(
            yaxis = dict(
                tickformat='$,.0f'
            )
        )
        
        for trace in fig.data:
            trace.update(hoverinfo='y')
            trace_color = trace.marker.color
            hover_template = f'<b style="color: {trace_color}">%{{customdata[0]}}, %{{customdata[1]}}</b><br>Per Capita GDP: $%{{y:,.0f}}<extra></extra>'
            trace.update(hovertemplate=hover_template)
    else:
        fig = px.scatter(
            df,
            x='gdpPercap',
            y=yaxis_chosen,
            color='continent',
            custom_data=['country', 'continent'],
            labels={
                'gdpPercap': 'Per Capita GDP',
                yaxis_chosen: options_dict.get(yaxis_chosen, yaxis_chosen)
            }
        )
        
        fig.update_layout(
            xaxis = dict(
                tickformat='$,.0f'
            )
        )
        
        pop_chosen = yaxis_chosen == 'pop'
        
        for trace in fig.data:
            trace_color = trace.marker.color
            if pop_chosen:
                hover_template = f'<b style="color: {trace_color}">%{{customdata[0]}}, %{{customdata[1]}}</b><br>Per Capita GDP: $%{{x:,.0f}}<br>{options_dict[yaxis_chosen]}: %{{y:,.0f}}<extra></extra>'
            else:
                hover_template = f'<b style="color: {trace_color}">%{{customdata[0]}}, %{{customdata[1]}}</b><br>Per Capita GDP: $%{{x:,.0f}}<br>{options_dict[yaxis_chosen]}: %{{y:.2f}}<extra></extra>'
            trace.update(hovertemplate=hover_template)
        
        if pop_chosen:
            fig.update_yaxes(type='log')
    
    fig.update_layout(
        legend_title='Continent',
        hoverlabel=dict(
            bgcolor='rgba(255, 255, 255, 0.75)'
        )
    )
    return fig


@callback(
    Output(component_id='tabular-data', component_property='rowData'),
    Output(component_id='tabular-data', component_property='columnDefs'),
    Input(component_id='my-scatter', component_property='selectedData')
)
def update_table(selected_data):
    if selected_data is None:
        return df.to_dict("records"), columnDefs
    
    countries_selected = []
    
    for points in selected_data['points']:
        countries_selected.append(points['customdata'][0])
    
    print(countries_selected)
    dff = df[df.country.isin(countries_selected)]
    
    return dff.to_dict("records"), columnDefs

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

Hey Wongcheefah,

Unfortunately, I don’t think there is a way to change them, unless you dig deep the source code.

As I understood hover template attribute affects only the “point type” data. For instance; only the outlier points above your purple box is affected by your code. That’s another reason why you did not have problem with updating hover info of scatter plots (points again).

Cheers!

1 Like

Hi Berbere,
Thank you for replying. You confirmed what I feared would be the case. Hopefully, that will change in a future release.

Cheers