Change Which Y-Axis a Trace is On with Plotly Buttons

Hello all.

Goal

Say I have a plot like this.

I would like to use the Plotly buttons to move a trace from the y1 axis to the y2 axis, like this.

I have already implemented similar functionality in Dash, but I would like this to use Plotly buttons so that I can save the plots as .html and not have a Python kernel supporting the application.

Question to Achieve that Goal

I have scoured the documentation but I can’t quite understand how the updatemenus work.
For instance, in the custom buttons example, how do you know what args you can specify within the updatemenu button.

# Add dropdown
fig.update_layout(
    updatemenus=[
        dict(
            type = "buttons",
            direction = "left",
            buttons=list([
                dict(
                    args=["type", "surface"],
                    label="3D Surface",
                    method="restyle"
                ),
                dict(
                    args=["type", "heatmap"],
                    label="Heatmap",
                    method="restyle"
                )
            ]),
            pad={"r": 10, "t": 10},
            showactive=True,
            x=0.11,
            xanchor="left",
            y=1.1,
            yanchor="top"
        ),
    ]
)

In this example they ues “type” and “surface”, but where specifically do those args go to? What other args could I specify? If someone could point me in the direction of additional documentation so that I could truly understand and then solve the problem myself, that would be great.

Ideas for Solution
One way to do this would be to create a button for each trace on the plot and when pressed that button goes into the properties for that traces and sets the ‘yaxis’ property to ‘y2’. Similar to what this line of code does.

fig.update_traces(yaxis='y2', selector=dict(name='Trace 2'))

Another idea for this is to just create two traces for all the columns of data, one on each y-axis, and only show the trace that is on the desired y-axis.

Thank you

Hi @CorMazz ,

Welcome to the forum!

To create custom button use restyle method as you mentions, like example that use “type” and “surface”.
I think you can set the args using yaxis properties as key and for the value if you have two traces you need to define what trace you want to set as y2 using list .

args=[{"yaxis":['y2','y']}],  # this line will change first trace to `y2` and second one remain at `y`.

Your first trace is line with Blue color and Red color is for second one.

If I can wrap it in runnable code:

import plotly.graph_objects as go

# Create random data with numpy
import numpy as np
np.random.seed(1)

N = 100
random_x = np.linspace(0, 1, N)
random_y = np.random.randn(N) + 5
random_y2 = np.random.randn(N) - 5

# Create traces
fig = go.Figure()
fig.add_trace(go.Scatter(x=random_x, y=random_y,
                    mode='lines',
                    line=dict(color='blue'),
                    name='Blue Line'))

fig.add_trace(go.Scatter(x=random_x, y=random_y2,
					line=dict(color='red'),
                    mode='lines', name='Red Line'))


print(fig)


# Add dropdown
fig.update_layout(
    yaxis=dict(
        title="yaxis",
        titlefont=dict(
            color="grey"
        ),
        tickfont=dict(
            color="grey"
        ),
    ),
    yaxis2=dict(
        title="yaxis2 ",
        titlefont=dict(
            color="grey"
        ),
        tickfont=dict(
            color="grey"
        ),
        overlaying="y",
        side="right",
    ),
    updatemenus=[
        dict(
            buttons=list([
            	dict(
                    args=[{"yaxis":['y','y']}],
                    label="Set All to y",
                    method="restyle"
                ),
                dict(
                    args=[{"yaxis":['y2','y']}],
                    label="Set Blue to y2",
                    method="restyle"
                ),
                dict(
                    args=[{"yaxis":['y','y2']}],
                    label="Set Red to y2",
                    method="restyle"
                )
            ]),
            direction="down",
            pad={"r": 10, "t": 10},
            showactive=True,
            x=0.1,
            xanchor="left",
            y=1.15,
            yanchor="top"
        ),
    ]
)

fig.show()

Hope this help.

@farispriadi

Hey, I really appreciate the quick turnaround and the response. Yes, this does almost exactly what I want, and I’m marking it as the solution because it does exactly what I asked for. Unfortunately, what I asked for, and what I wanted were slightly different :stuck_out_tongue:

Do you know of a way to make it so that I have a different button for each trace that toggles the axis for the trace? I have like 20 traces I’d like to do this for, so a dropdown is a bit restrictive. I don’t know if this is possible with the method you provided, since it requires the dropdown to give an option for each possible layout of the traces. Sorry for not making this clear in the original question.

Additionally, if you don’t mind answering:
How did you know that the “yaxis” arg would change the y-axis property for the traces on the plot? I don’t understand how the args are interpreted by the updatemenu.

How could I have figured this out for myself?

Thank you,
CorMazz

@Hi CorMazz,
The restyle method updates the trace attributes.
Any trace type has an attribute xaxis, and yaxis, to set the reference system that trace is drawn. The default values are: xaxis="x", yaxis="y". That’s why you did not understand because the default values have not been set. But when one restyles the plot, you pass through args the new axes.

For more information on the methods relayout, restyle, update, see these tutorials:
https://chart-studio.plotly.com/~empet/15608
https://chart-studio.plotly.com/~empet/15607/
https://chart-studio.plotly.com/~empet/15605

From this notebook https://chart-studio.plotly.com/~empet/15699/restyling-two-traces-in-a-fig/#/ you can learn how to add more buttons.

2 Likes

You are a steely-eyed missile man @empet

This explains everything I was confused about, and I think I can use the information you provided here

to create specific buttons which modify just the desired trace.

Thanks to both of you for the help @empet @farispriadi

Edit:

Here is the code that does exactly what I wanted.

import plotly.graph_objects as go

# Create random data with numpy
import numpy as np
np.random.seed(1)


N = 100  # number of points per trace
random_x = np.linspace(0, 1, N)

# generate k traces
k = 4
y_data_list = [np.random.randn(N) + np.random.randn() for _ in range(k)]

# Create the figure and add the traces
fig = go.Figure()

fig.add_traces(
    [go.Scatter(
        x=random_x,
        y=y_data,
        mode='lines',
        name=f'Trace {i}')
        for i, y_data in enumerate(y_data_list)
])

########################################################################################################################
# Create a Figure With Toggle Buttons
########################################################################################################################

# Create the toggle buttons
toggle_buttons = [
    dict(
        args=[{"yaxis": ['y2']}, i],  # change the y-axis property to y2 for the i'th trace
        args2=[{"yaxis": ['y']}, i],  # change the y-axis property back to y for the i'th trace upon unclicking
        label=f"Trace {i} Y2",
        method="restyle"
    )
    for i in range(len(y_data_list))
]

# Add the buttons
fig.update_layout(
    title="Individual Toggle Buttons",
    yaxis=dict(
        title="yaxis",
        titlefont=dict(
            color="grey"
        ),
        tickfont=dict(
            color="grey"
        ),
    ),
    yaxis2=dict(
        title="yaxis2 ",
        titlefont=dict(
            color="grey"
        ),
        tickfont=dict(
            color="grey"
        ),
        overlaying="y",
        side="right",
    ),
    updatemenus=[
        dict(
            type="buttons",  # Make it a button and not a dropdown
            active=1,  # Starts un-clicked by default
            buttons=toggle_buttons
        )
    ]
)

fig.show(renderer="browser")

########################################################################################################################
# Modify the Figure To Instead Have a Toggle Dropdown
########################################################################################################################

# Add the buttons
fig.update_layout(
    title="Toggle Dropdown Buttons",
    updatemenus=[
        dict(
            type="dropdown",  # Now make it a dropdown
            buttons=toggle_buttons  # Add all buttons
        )
    ]
)

fig.show(renderer="browser")
1 Like