Use Hover Trace As Input for Callback

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?

2 Likes