How to overlay two plots in same figure with slider

Hi everyone, I am having a hard time plotting two numpy arrays sharing the same figure and same slider.

Aim: Having two scatter plots in the same figure while using a slider in Plotly.

Expected behavior: Show a figure with two plots updating simultaneously and sharing the same “slider step”.

Current behavior: The slider steps over both scatter plots, separating them and showing one result at a time.

I attach below a minimal reproducible example adapted from the plotly documentation. Instead of simply plotting the sin(x), I also added a second plot with cos(x).
I tried using add_traces(), and also creating two separate traces and the updating them with fig = go.Figure(data=trace_list1+trace_list2) as shown here.

Any help would be much appreciated!

import plotly.graph_objects as go
import numpy as np

# Create figure
fig = go.Figure()

# Add traces, one for each slider step
for step in np.arange(0, 5, 0.5):
    fig.add_traces([
        go.Scatter(
            x=np.arange(0, 10, 0.01),
            y=np.sin(step * np.arange(0, 10, 0.01))),
        go.Scatter(
            x=np.arange(0, 10, 0.01),
            y=np.cos(step * np.arange(0, 10, 0.01)))])

# Make 10th trace visible
fig.data[10].visible = True

# Create and add slider
steps = []
for i in range(len(fig.data)):
    step = dict(
        method="update",
        args=[{"visible": [False] * len(fig.data)},
              {"title": "Slider switched to step: " + str(i)}],  # layout attribute
    )
    step["args"][0]["visible"][i] = True  # Toggle i'th trace to "visible"
    steps.append(step)

sliders = [dict(
    active=10,
    currentvalue={"prefix": "Frequency: "},
    pad={"t": 50},
    steps=steps
)]
fig.update_layout(
    sliders=sliders
)
fig.show()

Hi Simone, welcome to the community!

I’ve found this thread that has a solution:

So in your example above, you have a for loop adding a sin(x) trace and a cos(x) trace to fig.data every iteration. If you print out fig.data, you have something like this:

(
Scatter({
    'line': {'color': 'blue', 'width': 6},
    'name': 'sin(x) = 0.0',
    'visible': False,
    'x': array([0.  , 0.01, 0.02, ..., 9.97, 9.98, 9.99]),
    'y': array([0., 0., 0., ..., 0., 0., 0.])
}), 
Scatter({
    'line': {'color': 'red', 'width': 6},
    'name': 'cos(x) = 0.0',
    'visible': False,
    'x': array([0.  , 0.01, 0.02, ..., 9.97, 9.98, 9.99]),
    'y': array([1., 1., 1., ..., 1., 1., 1.])
}), 
Scatter({
    'line': {'color': 'blue', 'width': 6},
    'name': 'sin(x) = 0.5',
    'visible': False,
    'x': array([0.  , 0.01, 0.02, ..., 9.97, 9.98, 9.99]),
    'y': array([ 0.        ,  0.00499998,  0.00999983, ..., -0.96307117, -0.9617129 ,
                -0.96033059])
}), 
Scatter({
    'line': {'color': 'red', 'width': 6},
    'name': 'cos(x) = 0.5',
    'visible': False,
    'x': array([0.  , 0.01, 0.02, ..., 9.97, 9.98, 9.99]),
    'y': array([1.        , 0.9999875 , 0.99995   , ..., 0.26924695, 0.27405892,
                0.27886404])
}),
...
)

Then the 2nd for loop iterates over fig.data to create a list telling the slider which of the above traces should be visible depending on the step. Now let’s have a look at steps:

{'method': 'update', 'args': [{'visible': [True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 0'}]}
{'method': 'update', 'args': [{'visible': [False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 1'}]}
{'method': 'update', 'args': [{'visible': [False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 2'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 3'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 4'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 5'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 6'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 7'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 8'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 9'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 10'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 11'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 12'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False, False]}, {'title': 'Slider switched to step: 13'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False, False]}, {'title': 'Slider switched to step: 14'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False, False]}, {'title': 'Slider switched to step: 15'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False, False]}, {'title': 'Slider switched to step: 16'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False, False]}, {'title': 'Slider switched to step: 17'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True, False]}, {'title': 'Slider switched to step: 18'}]}
{'method': 'update', 'args': [{'visible': [False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, False, True]}, {'title': 'Slider switched to step: 19'}]}

Since you added 20 traces to fig.data (10 for sin(x), 10 for cos(x)), we end up with the list above. But every entry in said list only sets the visibility to True for one trace. But what you want is to set it to True for 2 traces at the same time (so traces 1 and 2, 3 and 4, 5 and 6, 7 and 8, and so on…).

Here’s my proposed solution (probably not the best, but it does it’s job):

# Create and add slider
steps = []
for i in range(len(fig.data)):
    if i % 2 == 0:
        step = dict(
            method="update",
            args=[{"visible": [False] * len(fig.data)},
                  {"title": "Slider switched to step: " + str(i/2)}],  # layout attribute
        )
        step["args"][0]["visible"][i] = True  # Toggle i'th trace to "visible"
        step["args"][0]["visible"][i+1] = True
        steps.append(step)

I added

if i % 2 == 0

to only add every 2nd trace to the steps list and

step["args"][0]["visible"][i+1] = True

to set the next trace to True as well.

One small change was also required for the text that is being displayed: str(i/2)

Let me know if that helps!

Dear Yan, thank you very much for your help. This works very well indeed (and if, let’s say, we want to plot 3-4 traces on the same plot, the idea works as well with little modifications).