Scatter plot click event not exact

I’m trying to return a value based on hoverData of Scatterplot . I refered this doc and tried to make myself but results returned not exactly. Below is my sample code:

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

PCA_table=pd.read_excel('https://github.com/hoatranobita/Jobs/blob/main/PCA_table.xlsx?raw=true')
external_stylesheets = ['https://codepen.io/chriddyp/pen/bWLwgP.css']

app = Dash(__name__, external_stylesheets=external_stylesheets)

df = pd.read_csv('https://plotly.github.io/datasets/country_indicators.csv')


app.layout = html.Div([
    html.Div([
        html.Div([
            dcc.Dropdown(
                options=PCA_table.columns.unique(),
                value='PC1',
                id='crossfilter-xaxis-column',
            )],style={'width': '49%', 'display': 'inline-block'}),
        html.Div([
            dcc.Dropdown(
                options=PCA_table.columns.unique(),
                value='PC2',
                id='crossfilter-yaxis-column'
            )], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
    ], style={'padding': '10px 5px'}),
    
    html.Div([
        dcc.Graph(
            id='crossfilter-indicator-scatter',
            hoverData={'points': [{'customdata': '184A1'}]}
        )], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
    html.Div([
        html.Div(id='x-time-series'),
    ], style={'display': 'inline-block', 'width': '49%'}),
])

@app.callback(
    Output('crossfilter-indicator-scatter', 'figure'),
    Input('crossfilter-xaxis-column', 'value'),
    Input('crossfilter-yaxis-column', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name):
    
    fig = px.scatter(PCA_table,x=PCA_table[xaxis_column_name], 
                          y=PCA_table[yaxis_column_name], 
                          color=PCA_table['Labels'],
                          labels=PCA_table['Columns'],
                          hover_name=PCA_table['Columns'],
                          title="2D PCA for Labels")
    fig.update_layout(clickmode='event')
    fig.update_traces(customdata=PCA_table['Columns'])
    fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')

    return fig

@app.callback(
    Output('x-time-series', 'children'),
    Input('crossfilter-indicator-scatter', 'hoverData'))
def update_y_timeseries(hoverData):
    country_name = hoverData['points'][0]['customdata']
    return html.Span(country_name)

if __name__ == '__main__':
    app.run_server(debug=False,port=1111)

And here is the result:

As you see hover_name and html.Span is different. And I need it to be the same. Thank you.

Looks like the same data point has different customdata and hovertext values.
I printed both when hovering above a point on your plot:

And from the dataset you provided, it looks like the correct value is the hovertext.

I’m not sure you need to use the customdata parameter. You can only use the hover_name parameter and hovertext
key in your data point:

app.layout = html.Div([
    html.Div([
        html.Div([
            dcc.Dropdown(
                options=PCA_table.columns.unique(),
                value='PC1',
                id='crossfilter-xaxis-column',
            )],style={'width': '49%', 'display': 'inline-block'}),
        html.Div([
            dcc.Dropdown(
                options=PCA_table.columns.unique(),
                value='PC2',
                id='crossfilter-yaxis-column'
            )], style={'width': '49%', 'float': 'right', 'display': 'inline-block'})
    ], style={'padding': '10px 5px'}),
    
    html.Div([
        dcc.Graph(
            id='crossfilter-indicator-scatter',
            #hoverData={'points': [{'customdata': '184A1'}]}
        )], style={'width': '49%', 'display': 'inline-block', 'padding': '0 20'}),
    html.Div([
        html.Div(id='x-time-series'),
    ], style={'display': 'inline-block', 'width': '49%'}),
])

@app.callback(
    Output('crossfilter-indicator-scatter', 'figure'),
    Input('crossfilter-xaxis-column', 'value'),
    Input('crossfilter-yaxis-column', 'value'))
def update_graph(xaxis_column_name, yaxis_column_name):
    
    fig = px.scatter(PCA_table,x=PCA_table[xaxis_column_name], 
                          y=PCA_table[yaxis_column_name], 
                          color=PCA_table['Labels'],
                          labels=PCA_table['Columns'],
                          hover_name=PCA_table['Columns'],
                          title="2D PCA for Labels")
    fig.update_layout(clickmode='event')
    #fig.update_traces(customdata=PCA_table['Columns'])
    fig.update_layout(margin={'l': 40, 'b': 40, 't': 10, 'r': 0}, hovermode='closest')

    return fig

@app.callback(
    Output('x-time-series', 'children'),
    Input('crossfilter-indicator-scatter', 'hoverData'))
def update_y_timeseries(hoverData):
    print(hoverData)
    if hoverData:
        country_name = hoverData['points'][0]['hovertext']
        return html.Span(country_name)

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

Here’s what I did:

→ removed the initial hoverData in your dcc.Graph component (I added an if hoverData exists condition to avoid the initial “non-existent input error message”).
→ removed the update_traces
→ displays the country name using the hovertextkey in the data point

And here’s the result:

plotly_community

2 Likes

Thank you for that, I changed as your suggestion and it worked.