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

Shared X-axis with Sub and Side-by-Side Plots and Click Events in HTML Output File

Hello,

I’m working to create a figure with multiple scatter subplots (~16-20) which are organized in multiple columns and rows. Each subplot has the same x-axis range, but different y-axes. I haven’t been able to find it, but I’m wondering if it’s possible to link all subplot x-axes such that when I zoom on one, the other plots will update? Below is an example of a truncated chart with multiple zoom levels in each chart. It would be great if they could have the same zoom level.

An alternative solution I’ve been looking into is using click events in the charts to get x-coordinates and then use fig.update_xaxes(), but the charts will be interacted with as html files and I haven’t found how to add click events to such files.

Below is the code I’m using to generate the current charts

import math
import pandas as pd
import plotly.graph_objects as go
import chart_definitions as cd
from plotly.subplots import make_subplots

#Get telemetry root folder
TELEMETRY_PATH = 'telemetry'

#Define number of subplot columns
MAX_COLUMNS = 4

#Instantiate holder for scatter objects
chart_holder = []

#Get map of chart ID's and names
charts = cd.chart_map()

#Display to user which plots are available
print('Chart ID: Chart Name')
for chart_id in charts:
    print(f'{chart_id}: {charts[chart_id]}')
    
#Query user for which charts to build
chart_selection = input('Please select from the above charts (using chart ID) the charts you would like to view. Please use csv format. ').split(',')
chart_selection = [int(n) for n in chart_selection]

#Get individual chart dictionaries which include csv locations for each metric
chart_dicts = cd.metric_selector(chart_selection)

#Determine number of rows from number of charts
rows = math.ceil(len(chart_dicts)/MAX_COLUMNS)

#Instantiate figure object
fig = make_subplots(rows=rows, cols=len(chart_dicts), shared_xaxes=True)

#Build charts
for i in range(len(chart_dicts)):
    #Get row id
    row = math.floor(i/MAX_COLUMNS) + 1
    
    #Build xaxis list
    xaxis = ['x']
    xaxis.extend([f'x{str(i)}' for i in range(1, len(chart_dicts))])
    
    #Actually build charts
    for metric in chart_dicts[i]['metrics']:
        path = f'{TELEMETRY_PATH}/' + chart_dicts[i]['metrics'][metric]['filename']
        print(f'Building subplot for {path}')
        df =  pd.read_csv(f'{TELEMETRY_PATH}/' + chart_dicts[i]['metrics'][metric]['filename'])[['timestamp', 'value']].sort_values(by='timestamp')
        chart_holder.append(fig.add_trace(go.Scatter(x=df['timestamp'], y=df['value'], name=metric, xaxis=xaxis[i]), row=row, col=i+1))
    
#Add hovermode and hide legend     
fig.update_layout(hovermode='x', showlegend=False)

#Write to html file for sharing
fig.write_html('Telemetry_figure.html', auto_open=True)

Hi @ngaudio welcome to the forum! You can link together the different xaxes by using the matches attribute (https://plot.ly/python/reference/#layout-xaxis-matches). See for example the code below (we should add one such example in https://plot.ly/python/axes/, https://plot.ly/python/facet-plots/ and https://plot.ly/python/subplots/)

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np

N = 100
x = np.linspace(0, 1, N)
fig = make_subplots(2, 2)
for i in range(1, 3):
    for j in range(1, 3):
        fig.append_trace(go.Scatter(x=x, y=np.random.random(N)), i, j)
fig.update_xaxes(matches='x')
fig.show()

If you are able to use a plotly express function with facets (see https://plot.ly/python/facet-plots/), then axes are already matched together by default.

Hi @Emmanuelle! Thank you very much for your response! I came across matches, but in my experimentation I couldn’t get it to work. Turns out I was implementing it wrong.

Anyway, your solution worked! Thank you very much!