Black Lives Matter. Please consider donating to Black Girls Code today.

Use Hover Trace As Input for Callback

Hello all,

I am attempting to use the SPLOM (https://plot.ly/python/splom/) graph on my app, but also want to generate another graph based on which column/row is currently being hovered over / Selected. Is there a way to identify which subplot is being hovered over from the new SPLOM?

For example if I had a SPLOM with 5 dimensions, this should create a 5x5 traces. Let us say I am hoving over data from the first row, third column, is there a way for me to get that information (first row, third column) and route it into a callbacks input?

Thanks for your help!

Hi @WolVes, it’s a bit more convoluted than I would have hoped, but below is an approach which should work in most cases. I adapted the first example of https://dash.plot.ly/interactive-graphing to a splom trace, with the following code

import json
from textwrap import dedent as d

import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output

external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = dash.Dash(__name__, external_stylesheets=external_stylesheets)

styles = {
    'pre': {
        'border': 'thin lightgrey solid',
        'overflowX': 'scroll'
    }
}


import plotly.express as px
df = px.data.iris()
fig = px.scatter_matrix(df)

app.layout = html.Div([
    dcc.Graph(
        id='basic-interactions',
        figure=fig,
    ),

    html.Div(className='row', children=[
        html.Div([
            dcc.Markdown(d("""
                **Hover Data**

                Mouse over values in the graph.
            """)),
            html.Pre(id='hover-data', style=styles['pre'])
        ], className='three columns'),

        html.Div([
            dcc.Markdown(d("""
                **Click Data**

                Click on points in the graph.
            """)),
            html.Pre(id='click-data', style=styles['pre']),
        ], className='three columns'),

        html.Div([
            dcc.Markdown(d("""
                **Selection Data**

                Choose the lasso or rectangle tool in the graph's menu
                bar and then select points in the graph.

                Note that if `layout.clickmode = 'event+select'`, selection data also 
                accumulates (or un-accumulates) selected data if you hold down the shift
                button while clicking.
            """)),
            html.Pre(id='selected-data', style=styles['pre']),
        ], className='three columns'),

        html.Div([
            dcc.Markdown(d("""
                **Zoom and Relayout Data**

                Click and drag on the graph to zoom or click on the zoom
                buttons in the graph's menu bar.
                Clicking on legend items will also fire
                this event.
            """)),
            html.Pre(id='relayout-data', style=styles['pre']),
        ], className='three columns')
    ])
])


@app.callback(
    Output('hover-data', 'children'),
    [Input('basic-interactions', 'hoverData')])
def display_hover_data(hoverData):
    return json.dumps(hoverData, indent=2)


@app.callback(
    Output('click-data', 'children'),
    [Input('basic-interactions', 'clickData')])
def display_click_data(clickData):
    return json.dumps(clickData, indent=2)


@app.callback(
    Output('selected-data', 'children'),
    [Input('basic-interactions', 'selectedData')])
def display_selected_data(selectedData):
    return json.dumps(selectedData, indent=2)


@app.callback(
    Output('relayout-data', 'children'),
    [Input('basic-interactions', 'relayoutData')])
def display_relayout_data(relayoutData):
    return json.dumps(relayoutData, indent=2)


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

then when you hover on a point you will see the following hoverData (you could also just print it in a callback, the fancy formatting of the app is just for convenience).

{
  "points": [
    {
      "curveNumber": 0,
      "pointNumber": 8,
      "pointIndex": 8,
      "x": 4.4,
      "y": 2.9,
      "dimensions[0].values": 4.4,
      "dimensions[1].values": 2.9,
      "dimensions[2].values": 1.4,
      "dimensions[3].values": 0.2,
      "dimensions[4].values": "setosa",
      "dimensions[5].values": 1
    }
  ]
}

So one thing you could do is check to which dimensions the values of x and y correspond, by comparing them to the dimensions[i].values which are also given. I must admit that this breaks down when a given point has equal values for different dimensions, maybe you can issue a warning in this case?

1 Like

Thanks for your help Emmanuelle. I had been playing around with a similar idea a bit earlier! Unfortunately, the goal is to use it for a wide range of similar data. Thus, knowing the trace is necessary.

I am currently looking into seeing if it is possible to edit the primary hoverData response to provide the trace value. Obviously this is less than ideal as it requires my own edits to the package, but I am not seeing a more obvious approach.