Linked Graphs from x-y scatter to x vs t and y vs t

There used to be a demo on linked graphs on plotly website, this is nowhere to be found.
Problem Statement:

  1. I have a x-y scatter plot
  2. I would like to link two, x vs time and y vs time to x-y plot, meaning when I click on a point on the x-y plot it should draw horizontal and vertical dotted lines into two separate plots x-t and y-t.

x-y plot will be at the upper left diagonal position and x-t plot on right hand side and y-t below x-y plot.

Can someone provide guidance on how to achieve the same?

Hi @vivek22, I would use dash. A grid of three dcc.Graphs() and trigger a callback on click on the x-y graph.

Here is an example how to use hoverData, In your case you would have to use the clickData.

I get following error

ImportError: cannot import name ‘Patch’ from ‘dash’ (C:\Users\suche\miniconda3\lib\site-packages\dash_init_.py)

I already did pip3 install -U dash. I have dash-2.14.2

Are you using Jupiter? Did you restart your kernel?

What version of jupyter are you using?

Yes I am using Jupyter Notebook. When I type Jupyter Notebook version, it says [C 07:08:18:815 NotebookApp]

I am very sorry! It seems to do something (not exactly what I want) today. Please see the snapshot

I do not see three linked plots. May be I I am not communicating the problem correctly. I will cook up a detailed example.

Here are the details of the problem


Step-1: One can do x-y plot of Inflation vs FF_rate
What I want is when I hover over (or click) on x-y plot, I want to see corresponding points of two separate plots 1. Inflation vs date 2. FF_rate vs Date

Yes, it’s not what you wanted to achieve. It was just an example which you could adapt to solve your problem.

I am unable to use your example. Could you please elaborate how I could use it for mine?

OK. You could start substituting the data with yours and create the x-y chart. Then copy the dcc.Graph() two times, change the id’s to a unique name and create the other two graphs.

I will try it tomorrow and let you know if I have any questions. Thanks!

Hi there, here a mock up:

from dash import html, dcc, Input, Output, Patch
import plotly.graph_objects as go
import dash

# dummy figure
fig = go.Figure(go.Scatter(x=[1, 2, 3], y=[1, 2, 3]))


def v_line(x):
    shapes = [
        {'type': 'line',
         'x0': x,
         'x1': x,
         'xref': 'x',
         'y0': 0,
         'y1': 1,
         'yref': 'y domain'
         }
    ]

    return shapes


def h_line(y):
    shapes = [
        {'type': 'line',
         'x0': 0,
         'x1': 1,
         'xref': 'x domain',
         'y0': y,
         'y1': y,
         'yref': 'y'
         }
    ]

    return shapes


app = dash.Dash(
    __name__,
    external_stylesheets=[
        "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css",
    ]
)

app.layout = html.Div([
    html.Div(
        className='row',
        children=[
            html.Div(
                className='col-6',
                children=dcc.Graph(id='graph_1', figure=fig)
            ),
            html.Div(
                className='col-6',
                children=dcc.Graph(id='graph_2', figure=fig)
            )
        ]
    ),
    html.Div(
        className='row',
        children=[
            html.Div(
                className='col-6',
                children=dcc.Graph(id='graph_3', figure=fig)
            ),
            html.Div(
                className='col-6',
            )
        ]
    )
])


@app.callback(
    Output('graph_2', 'figure'),
    Output('graph_3', 'figure'),
    Input('graph_1', 'hoverData'),
    prevent_initial_call=True
)
def update(hoverData):
    # get hover coordinate in x direction
    x = hoverData['points'][0]['x']
    y = hoverData['points'][0]['y']

    # create patches
    patch_x = Patch()
    patch_y = Patch()
    
    # add lines
    patch_x['layout']['shapes'] = v_line(x)
    patch_y['layout']['shapes'] = h_line(y)

    return patch_x, patch_y


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


I got most it working with your help, except hovering part.

Here is my code

from dash import Dash, html, dcc, Output, Input, Patch
import os.path
import plotly.graph_objs as go
import plotly.offline as py
import datetime
import calendar
import pandas as pd
import numpy as np
import sys
import openpyxl
from ipywidgets import interactive, HBox, VBox
from datetime import date, datetime
from datetime import timedelta    #New addition
from os.path import exists
from colorama import Fore, Back, Style
from openpyxl import Workbook, load_workbook
from pandas_datareader import data as pdr  #8/6/23 Vivek R. Dabholkar
from plotly.subplots import make_subplots
import dash
#
spath = 'C:\\Daily_Stock_Report\\Performance\\Excel\\' 
filename = spath + "Indices.xlsx"
df = pd.read_excel(filename, sheet_name="Close", index_col=None)
#
df['Date'] = pd.to_datetime(df.Date)
df['month'] = df['Date'].dt.strftime('%b')
df['year'] = df['Date'].dt.strftime('%Y')
#
# dummy figure
fig = go.Figure(go.Scatter(x=df["^TNX"], y=df["^GSPC"],mode='markers',marker_color="red"))
fig.update_yaxes(type="log")
fig.update_layout(title_text="S&P500 vs 10Yr Treasuries",
                  title_font_size=14)
#
fig1 = go.Figure(go.Scatter(x=df["Date"], y=df["^GSPC"],mode='markers',marker_color="green"))
fig1.update_yaxes(type="log")
fig1.update_layout(title_text="S&P500 vs Time",
                  title_font_size=14)
fig2 = go.Figure(go.Scatter(x=df["Date"], y=df["^TNX"],mode='markers',marker_color="blue"))
fig2.update_layout(title_text="^TNX vs Time",
                  title_font_size=14)
#
ptitle = "Linked_Plots"
figname = spath + ptitle + ".html"
# print('Writing ', figname)
fig.write_html(figname)

def v_line(x):
    shapes = [
        {'type': 'line',
         'x0': x,
         'x1': x,
         'xref': 'x',
         'y0': 0,
         'y1': 1,
         'yref': 'y domain'
         }
    ]

    return shapes


def h_line(y):
    shapes = [
        {'type': 'line',
         'x0': 0,
         'x1': 1,
         'xref': 'x domain',
         'y0': y,
         'y1': y,
         'yref': 'y'
         }
    ]

    return shapes


app = dash.Dash(
    __name__,
    external_stylesheets=[
        "https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css",
    ]
)

app.layout = html.Div([
    html.Div(
        className='row',
        children=[
            html.Div(
                className='col-6',
                children=dcc.Graph(id='graph_1', figure=fig)
            ),
            html.Div(
                className='col-6',
                children=dcc.Graph(id='graph_2', figure=fig1)
            )
        ]
    ),
    html.Div(
        className='row',
        children=[
            html.Div(
                className='col-6',
                children=dcc.Graph(id='graph_3', figure=fig2)
            ),
            html.Div(
                className='col-6',
            )
        ]
    )
])


@app.callback(
    Output('graph_2', 'figure'),
    Output('graph_3', 'figure'),
    Input('graph_1', 'hoverData'),
    prevent_initial_call=True
)
def update(hoverData):
    # get hover coordinate in x direction
    x = hoverData['points'][0]['x']
    y = hoverData['points'][0]['y']

    # create patches
    patch_x = Patch()
    patch_y = Patch()
    
    # add lines
    patch_x['layout']['shapes'] = v_line(x)
    patch_y['layout']['shapes'] = h_line(y)

    return patch_x, patch_y


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

Can I buy you an Electronic Coffee please thru paypal?

I am also unable to save the 3-combo plot into html file

What do you mean by that?

That’s not possible with the solution I proposed.

You mean Dash plots cannot be saved as htmls? For some reason, it plots twice!