✊🏿 Black Lives Matter. Please consider donating to Black Girls Code today.
⚾️ It's finally Baseball season! Root for the home team... & Register for our Sports Analytics Webinar!

Highlight Group of Points on Hover

Hi -

I’m trying to do something similar to this, only with Plotly.py. In other words, I’d like to create a scatterplot with grouped points where all points within the group are highlighted (and/or enlarged) and display a tooltip on hover.

Is there an easy way to do this with Plotly.py? And if not, is there a hard way to do it? :slightly_smiling_face:

Toy example here (minus the desired hover behavior, obviously):

import plotly.io as pio

family = ['Brown', 'Brown', 'Brown', 'Smith', 'Smith', 'Smith', 'Jones', 'Jones', 'Jones', 'Lee', 'Lee', 'Lee']
subject = ['Moe','Larry','Curly','Moe','Larry','Curly','Moe','Larry','Curly','Moe','Larry','Curly']
score = [1,6,2,8,3,9,4,5,1,5,2,8]

data = [dict(
  type = 'scatter',
  x = subject,
  y = score,
  mode = 'markers',
  transforms = [dict(
    type = 'groupby',
    groups = family,
    styles = [
        dict(target = 'Smith', value = dict(marker = dict(color = 'blue'))),
        dict(target = 'Brown', value = dict(marker = dict(color = 'red'))),
        dict(target = 'Jones', value = dict(marker = dict(color = 'black'))),
        dict(target = 'Lee', value = dict(marker = dict(color = 'green')))
    ]
  )]
)]

fig_dict = dict(data=data)
pio.show(fig_dict, validate=False)

Thanks for your help!

2 Likes

I should also mention that this plot will ultimately be embedded in a Dash app, so perhaps it’s not necessary for it to be done purely w/ Plotly.py. Maybe I could use the event data described here to achieve this effect.

I’m also open to writing some custom JS within the Dash app following the approach here (which achieves the exact effect I’m looking for).

Any thoughts on what approach would be best?

I’ve stumbled upon this problem myself an my solution was, indeed, to use Dash!

Here is a proof of concept:

import dash
import dash_core_components as dcc
import dash_html_components as html
import pandas as pd
import plotly.graph_objects as go
from dash.dependencies import Input, Output, State

app = dash.Dash(
    name=__name__,
    external_stylesheets=['https://codepen.io/chriddyp/pen/bWLwgP.css']
)

app.layout = html.Div([
    html.Div(
        children=[
            dcc.Graph(
                id='plot',
            )
        ],
    )
])

df = pd.DataFrame(dict(
    family=['Brown', 'Brown', 'Brown', 'Smith', 'Smith', 'Smith', 'Jones', 'Jones', 'Jones', 'Lee', 'Lee', 'Lee'],
    subject=['Moe', 'Larry', 'Curly', 'Moe', 'Larry', 'Curly', 'Moe', 'Larry', 'Curly', 'Moe', 'Larry', 'Curly'],
    score=[1, 6, 2, 8, 3, 9, 4, 5, 1, 5, 2, 8],
))

DEFAULT_SIZE = 10
HIGHLIGHT_SIZE = 30
styles = {
    'Smith': dict(marker=dict(color='blue', size=DEFAULT_SIZE)),
    'Brown': dict(marker=dict(color='red', size=DEFAULT_SIZE)),
    'Jones': dict(marker=dict(color='black', size=DEFAULT_SIZE)),
    'Lee': dict(marker=dict(color='green', size=DEFAULT_SIZE)),
}

SELECTED = None


@app.callback(
    output=Output('plot', 'figure'),
    inputs=[Input('plot', 'hoverData')],
    state=[State('plot', 'figure')]
)
def update_graph(hover_data, fig):
    global SELECTED
    if hover_data is not None:
        if SELECTED is not None:
            fig['data'][SELECTED]['marker']['size'] = DEFAULT_SIZE

        family = hover_data['points'][0]['customdata']
        SELECTED = ix = [scatter['name'] for scatter in fig['data']].index(family)
        fig['data'][ix]['marker']['size'] = HIGHLIGHT_SIZE
    else:
        fig = go.Figure()
        for family, subjects in df.groupby('family'):
            fig.add_scatter(
                x=subjects.subject,
                y=subjects.score,
                name=family,
                customdata=subjects.family,
                mode='markers',
                **styles[family]
            )
        # https://community.plotly.com/t/preserving-ui-state-like-zoom-in-dcc-graph-with-uirevision-with-dash/15793
        fig.update_layout(height=1000, hovermode='closest', uirevision='static')
    return fig


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

neat solution @konichuvak !

I am looking for something very similar.

instead of making the points larger on selection (as in @konichuvak’s example) I want to show the tooltip (i.e. hoverinfo) for all points within the same group (in this example the family).

Does anyone know a way on how to achieve this?