23

I have successfully created plotly graphs from python, and gone as far as creating custom html tooltips for the datapoints. But I haven't succeeded in adding functionality to open a link if someone clicks on a datapoint. What I would like is a separate tab to pop up showing more information linked to that particular data point.

Can anyone think of a way to achieve this with plotly?

wtfastro
  • 333
  • 1
  • 2
  • 6
  • 1
    Try binding to click events: https://plot.ly/python/click-events/ – Boedy Sep 17 '18 at 21:58
  • 1
    I wonder, if it's already possible to use these click events from the r interface? I have a link for each datapoint, that I would like to be opened "on click" . – stats-hb Feb 06 '19 at 23:01
  • The answer to this question has changed. sigma's answer should be the accepted one now: https://stackoverflow.com/a/68397006/2474025 – phobic Jan 30 '23 at 21:24

6 Answers6

11

This is a bit of a work around, but it does achieve the desired functionality

You can put some html into annotations. This includes hyper links of the form Text.

If you want to click on a point and not text, you can make an annotation of an empty string Text = " " that lies directly over your data point.

I tend to make my plots using the python API, so the code for the annotation would be of the form:

plotAnnotes = []

plotAnnotes.append(dict(x=xVal[i],
                        y=yVal[i],
                        text="""<a href="https://plot.ly/">{}</a>""".format("Text"),
                        showarrow=False,
                        xanchor='center',
                        yanchor='center',
                        ))

and in the layout include annotations=plotAnnotes. The values of xVal[i] and yVal[i] would come from your data.

JimInCanada
  • 153
  • 1
  • 10
  • Worked like a charm – Jesper - jtk.eth Dec 27 '16 at 00:48
  • Thanks for the suggestion. However, does it really scale in your case? I have few thousands of dots and my plot (html exported) went from ~5 Mb to ~1 Gb ...not really feasible ... – any other suggestions? – jjrr Apr 29 '18 at 17:05
5

It's not quite possible yet, but the best option might be to include a link in the text as hover, here is an example: https://plot.ly/~chris/2540 (click the Code tab to see how to replicate the graph)

Tom Wainwright
  • 648
  • 1
  • 6
  • 15
  • 24
    Yeah I was able to do that through normal html, but the problem is, the tooltip disappears as soon as one moves to the url, meaning one can't click on it. Is there any way to make the tooltip linger longer or something similar? – wtfastro Aug 07 '14 at 23:30
  • 5
    @Tom: was there an improvement in that direction? – thanks – jjrr Apr 29 '18 at 17:06
5

If you're using Dash, you can store the url in the customdata field and retrieve it in a callback that processes clickData input as follows.

import webbrowser
import dash
from dash.exceptions import PreventUpdate
import dash_core_components as dcc
from dash.dependencies import Input, Output
import plotly.express as px
import pandas as pd

app = dash.Dash(__name__)
df = pd.DataFrame(
    dict(
        x=[1, 2],
        y=[2, 4],
        urls=["https://www.google.com","https://plotly.com/dash/"],
    )
)
app.layout = html.Div(
    [
        dcc.Graph(
            id="graph-id",
            figure=px.scatter(data_frame=df, x="x", y="y", custom_data=("urls",)),
        ),
    ]
)

@app.callback(
        Output('graph-id', 'figure'), 
        [Input('graph-id', 'clickData')])
        def open_url(clickData):
            if clickData != None:
            url = clickData['points'][0]['customdata'][0]
            webbrowser.open_new_tab(url)
        else:
            raise PreventUpdate
sigma1510
  • 1,165
  • 1
  • 11
  • 26
  • This is the best answer to date. Scalable and simple. – dejuknow Oct 09 '21 at 01:28
  • 1
    Still the best answer but some slightly changes need to work with (at least) dash 2.7: The 'children' property is now replaced with 'figure' and the clickdata event is not a 'null' string but a proper null value when empty – mazelx Nov 06 '22 at 22:11
2

There is a workaround with FigureWidget here that worked for me.

tmsss
  • 1,979
  • 19
  • 23
1

Using the annotation feature mentioned by @JimInCanada, I managed to embed data-point specific URL for a context sensitive launch. For illustration, I am using below gapminder built-in data in the Plotly Express library.

The data is a country specific per-capita GDP, life expectancy and population over different years. The code creates a scatter object, updates the layout with some basic details and most importantly, iterates through the data rows to add an annotation dictionary with x and y coordinates and key text linking a data-point specific URL. The x-axis is logarithmic and hence the use of math.log10(x) for the x value.

Here is the full code:

import math

df = px.data.gapminder().query("year==2007 and continent=='Asia'")
fig = px.scatter(df, x="gdpPercap", y="lifeExp", color="lifeExp", \
                 size="pop", log_x=True, size_max=60)

fig.update_layout(
    height=800,width=1200,
    title_text='GDP and Life Expectancy (Asia, 2007)'
)
for idx in df.index:
    url="<a href='https://en.wikipedia.org/wiki/Demographics_of_"+df['country'][idx]+"' target='_blank'>"+df['country'][idx]+"</a>"
    fig.add_annotation(dict(x=math.log10(df['gdpPercap'][idx]),
                            y=df['lifeExp'][idx],
                            showarrow=False,
                            text=url,
                            xanchor='auto',
                            yanchor='auto'))
fig.show()

Here is the resulting plot showing the pointer and a data-point specific URL:

enter image description here

If anyone is interested in more details, I wrote this article in medium.

Heelara
  • 861
  • 9
  • 17
0

I reckon that your best bet is to embed the chart in a webpage and use the PostMessage API to listen for click events.

Plotly have made a couple of tutorials; there's one for plotly.js, and one for an embedded chart.

Hope these help!

Rytchbass
  • 406
  • 1
  • 5
  • 11