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

How to draw a vertical line ontop of a scatter plot?

I have a number of scatter plots each plotted against time on the x-axis.

I want to show a vertical line at the “current” time in each of the plots so that when the current time changes (in an animation) the line moves across each of the plots. The vertical line should span the full height of each plot’s current visible area but no more.

How could I implement this using plotly.js?

Hi @marekKnows.com,

I have no experience with plotly.js, but I succeeded with plotly.py to animate both a timeseries and a vertical line
at the actual date. I transformed the Python code to a low level one, to be as close as possible to plotly.js.
The trick is to reference the vertical line to the same xaxis as the timeseries, but to a secondary yaxis2 (placed at right side of the plot window and set invisible).
This is the animation https://plotly.com/~empet/15621/#/, and this the corresponding code:

import numpy as np
from datetime import datetime

dates = [datetime(2020, 1, k)  for k in range(1, 32)]+ \
        [datetime(2020, 2, k)  for k in range(1, 30)]                              

d = {'date': dates,
     'y':10+8*np.random.rand(60)}

ymin, ymax = d['y'].min(), d['y'].max()

trace0 = {'type': 'scatter',
          'x': [d['date'][0]],
          'y': [d['y'][0]], 
          'mode':'lines',
          'line': {'color': 'blue'},
          'yaxis': 'y'}
                             
trace1 = {'x': [d['date'][0], d['date'][0]],
          'y': [0, 1],
           'mode': 'lines',
           'line': {'color': 'red', 'width': 2},
           'showlegend': False,
           'xaxis': 'x',
           'yaxis': 'y2'}
           

layout = {'height': 450,
          'updatemenus': [{'buttons': [{'args': [None, 
                                                 {'frame': {'duration': 50,
                                                            'redraw': False}, 
                                                            'transition': {'duration': 0}, 
                                                            'fromcurrent': True,
                                                            'mode': 'immediate'}],
                                       'label': 'Play',
                                       'method': 'animate'}],
                         'showactive': False,
                         'type': 'buttons',
                         'x': -0.1,
                         'xanchor': 'right',
                         'y': 1.05,
                         'yanchor': 'top'}],
        'width': 800,
        'xaxis': {'anchor': 'y',
                    'domain': [0.0, 0.94],
                    'range': [datetime(2020, 1, 1), datetime(2020, 2, 29)]},
        'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0], 'range': [ymin, ymax]},
        'yaxis2': {'anchor': 'x', 
                     'fixedrange': True,
                     'overlaying': 'y', 
                     'range': [0, 1],
                     'side': 'right', 
                     'visible': False}#THE seondary yaxis is invisible
    }

n_frames =60
frames = []
for k in range(1, n_frames):
    frames.append({'data': [{'type': 'scatter',
                             'x': d['date'][:k+1],
                             'y': d['y'][:k+1]},
                            
                            {'type': 'scatter',
                              'x': [d['date'][k], d['date'][k]]}],
                          'traces':[0,1]})
fig ={'data': [trace0, trace1],
      'layout': layout,
      'frames': frames}

Thanks for the sample code. My scatter plots will have data on both the primary and secondary yaxis … So I’m not sure if I can do this unless there is a third y axis that I can use?

There is no third yaxis. In this case set yaxis2 visible, of the fixedrange [y2min-h, y2max+h], for some h
where y2min, y2max are the min, respectively max y-values for the scatter line referenced to xaxis, yaxis2. The vertical red line is also referenced to these axes.

https://plotly.com/~empet/15624/#/

I’m trying to understand the code that you posted, I don’t know python… I don’t know if the code shown is clipped… as at the bottom it doesn’t look like you are doing anything with the fig variable.

Are you generating an array of values for each frame in that for loop? So in frame 1, trace0 would contain 1 data point, in frame 2 trace0 would contain 2 data points, frame 3, 3 data points etc… is that correct?

Then looking at trace1 (the red line), you are only setting the x values so that the line will move… correct?

Since I need to have traces on both the primary and secondary yaxis that means I’ll need to iterate over all the data at each frame, to find the min and max values as you suggested. That seems really inefficient especially considering my data. I display 1000’s of data points in the plot window and the time range moves. so initially the plot may be showing data from t=0 to t=200seconds, but at a later point in time, this x range will change to show data from t=100 to t=300 seconds etc. Having to find the max and min values in all the data isn’t going to work for me. I like how in the simple example, you were able to keep the y2 range at 0 to 1 so it never needs to be updated.

I figured out a way to do what I want with a third y axis:

vertical line in plot https://codepen.io/mmakrzem/pen/ZEbBENK?editors=1010 via @CodePen

Have you seen this animation https://plotly.com/~empet/15624/#/?
Here I used only 2 yaxes ( Python code https://plotly.com/~empet/15627).
To answer your last question: yes, in the for loop each frame contains one more point compared to the previous one.