Send secondary axis plots to the background

Hey there,

Iโ€™ve been playing a bit with plotly and I managed to pretty much create the graph I wanted: a graph with a secondary axis where

  1. the first axis (on the left) is for a scatter plot;
  2. the secondary axis (on the right) is for a bar plot.

The only issue I have is that the plot on the secondary axis seems to come on top of the plot from the first axis, which is deteriorating the readability of the graph. When I reverse the axis, the same happens: the secondary axis plot comes on top, whatever the order of plotting. Here is an illustration.

Does someone have a solution to plot bars on the secondary axis while keeping them in the background?

Many thanks for your help.

Nobody has info about my issue? Thanks very much for your help.

Hi @ama1, welcome to the forum! If you want the left axis to be on top, you can use the side attribute of go.layout.YAXis as follows:

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

# Create figure with secondary y-axis
fig = make_subplots(specs=[[{"secondary_y": True}]])

# Add traces
fig.add_trace(
    go.Scatter(x=[1, 2, 3], y=[40, 50, 60], name="right axis data"),
    secondary_y=False,
)

fig.add_trace(
    go.Bar(x=[2, 3, 4], y=[4, 5, 6], name="left axis data"),
    secondary_y=True,
)

# Add figure title
fig.update_layout(
    title_text="Double Y Axis Example",
    yaxis=dict(title_text="<b>right</b> yaxis title", 
               side='right'
              ),
    yaxis2=dict(title_text="<b>left</b> yaxis title",
               side='left')
)

# Set x-axis title
fig.update_xaxes(title_text="xaxis title")


fig.show()
1 Like

Hi @Emmanuelle. I tried your solution and it totally fixed the problem. Thanks a lot.

For reference, is it the normal/expected behavior that the graphs plotted on the secondary axis appear on top of those of the primary axis?

Hi Emma, I tried this, but the secondary axis still overlays on primary axis. any clues? see below code, yaxis2 is getting overlaid on yaxis

fig = make_subplots(
    rows=3, cols=4, shared_xaxes=True, print_grid=True,
    vertical_spacing=0.03, horizontal_spacing=0.12,    
    specs=[[{"colspan": 3, "secondary_y":True}, None, None, {"secondary_y":True}],
           [{"colspan": 3, "secondary_y":True}, None, None, {"secondary_y":True}],
           [{"colspan": 3, "secondary_y":True}, None, None, {"secondary_y":True}]]
        )
   
    config = dict({'scrollZoom': True})
    fig.layout['xaxis']['matches'] = 'x5'
    fig.layout['xaxis3']['matches'] = 'x5'
    
    fig.layout['xaxis2']['matches'] = 'x5'
    fig.layout['xaxis4']['matches'] = 'x5'
    fig.layout['xaxis6']['matches'] = 'x5'
    
    fig.layout['xaxis']['showticklabels'] = False
    fig.layout['xaxis3']['showticklabels'] = False
    fig.layout['xaxis5']['showticklabels'] = True
    
    fig.layout['xaxis2']['showticklabels'] = False
    fig.layout['xaxis4']['showticklabels'] = False
    fig.layout['xaxis6']['showticklabels'] = True
    
    fig.layout['xaxis']['type'] = 'category'
    fig.layout['xaxis2']['type'] = 'category'
    fig.layout['xaxis3']['type'] = 'category'
    fig.layout['xaxis4']['type'] = 'category'
    fig.layout['xaxis5']['type'] = 'category'
    fig.layout['xaxis6']['type'] = 'category'
    
    fig.layout['yaxis']['side'] = 'left'
    fig.layout['yaxis2']['side'] = 'right'    
    fig.update_xaxes(dtick = 365 *24 * 60)

@new_learner
The side is assigned as expected, but with your code you cannot realise that itโ€™s OK.
your lines:

fig.layout['yaxis']['side'] = 'left'
fig.layout['yaxis2']['side'] = 'right'   

are un-necessary because, by default yaxis is at left, while the secondary yaxis2 at right.
I removed your lines that are not relevant to illustrate that yaxes are assigned correctly. Here is the default case (I set yaxes range to be more convincing):

import plotly.graph_objects as go
from plotly.subplots import make_subplots
import numpy as np
fig = make_subplots(
    rows=3, cols=4, shared_xaxes=True, print_grid=True,
    vertical_spacing=0.05, horizontal_spacing=0.12,    
    specs=[[{"colspan": 3, "secondary_y":True}, None, None, {"secondary_y":True}],
           [{"colspan": 3, "secondary_y":True}, None, None, {"secondary_y":True}],
           [{"colspan": 3, "secondary_y":True}, None, None, {"secondary_y":True}]]
        )
   
config = dict({'scrollZoom': True})
fig.layout['xaxis']['matches'] = 'x5'
fig.layout['xaxis3']['matches'] = 'x5'
    
fig.layout['xaxis2']['matches'] = 'x5'
fig.layout['xaxis4']['matches'] = 'x5'
fig.layout['xaxis6']['matches'] = 'x5'
    
fig.layout['xaxis']['showticklabels'] = False
fig.layout['xaxis3']['showticklabels'] = False
fig.layout['xaxis5']['showticklabels'] = True
    
fig.layout['xaxis2']['showticklabels'] = False
fig.layout['xaxis4']['showticklabels'] = False
fig.layout['xaxis6']['showticklabels'] = True
    
for k in range(1, 4):
    fig.add_trace(go.Scatter(x=np.arange(1,  6), y=(1+k)*np.random.rand(5)), k, 1, secondary_y=True)
for k in range(1, 4):
    fig.add_trace(go.Scatter(x=np.arange(1,  6), y=2*np.random.rand(5)), k, 1, secondary_y=False)  
fig.update_layout(yaxis2_range=[0,2], yaxis6_range=[0,3], yaxis10_range=[0,4]) 
for k in [2, 6, 10]:
    fig.update_layout{f"yaxis{k}": {"title":{"text": f'y{k}' 
                                              }}})

Now let us revert the position of two yaxes associated to the same traces:

fig.update_layout(yaxis9_side="right", yaxis10_side="left")