Show xaxis on both sides (Top and bottom)

Hello
I have some subplots that share the Y_axix and I am trying to show the X-axis on both sides of each subplot.

fig = make_subplots(
          rows=1, cols=3,
          specs=[[{'rowspan': 1, 'colspan': 1}, {'rowspan': 1, 'colspan': 1}, {'rowspan': 1, 'colspan': 1}]],
          horizontal_spacing =0.1, shared_yaxes=True)
fig.add_trace(go.Scatter(y=df1["number"], x = round(df1['1value'],1), mode = 'lines'), row=1, col=1)
fig.add_trace(go.Scatter(y=df1["number"], x = round(df1['2value'],1), mode = 'lines'),row=1, col=1)
fig.add_trace(go.Scatter(y=df1["number"], x = round(df1['3value'],1), mode = 'lines'),row=1, col=1)

fig.add_trace(go.Scatter(y=df2["number"], x = df2['1value'], mode = 'lines'), row=1, col=2)
fig.add_trace(go.Scatter(y=df2["number"], x = df2['2value'], mode = 'lines'), row=1, col=2)
fig.add_trace(go.Scatter(y=df2["number"], x = df2['3value'], mode = 'lines'), row=1, col=2)
    
fig.add_trace(go.Scatter(y=df3["number"], x = df3['1value'], mode = 'lines'), row=1, col=3)
fig.add_trace(go.Scatter(y=df3["number"], x = df3['2value'], mode = 'lines'), row=1, col=3)
fig.add_trace(go.Scatter(y=df3["number"], x = df3['3value'], mode = 'lines'), row=1, col=3)

fig.show()

So I need to show the x-axis on the top and bottom of the plots.

Any suggestions?

Hi @smail

Does this post answer your question?

1 Like

In fact, I get the axis on both sides of one graph only, and unfortunately, the first plot disappears!

fig = make_subplots(rows=1, cols=3,  shared_yaxes=True)
print(fig.layout)

fig.add_trace(go.Scatter(x=Y_axeline, y=X_axe, line = dict(color = '#666768', width=0.5),	fill=None,mode='lines',line_color='black', showlegend=False), row=1, col=1)  #trace0
fig.add_trace(go.Scatter(x=Y_axegrn , y=X_axe, line = dict(color = '#666768', width=0.5), fill='tonexty', fillcolor='rgba(0,250,0,0.4)', mode='lines', showlegend=False), row=1, col=1) # fill area between trace0 and trace1
fig.add_trace(go.Scatter(x=Y_axeline, y=X_axe, line = dict(color = '#666768', width=0.5),	fill=None, mode='lines', line_color='black',showlegend=False ), row=1, col=1) #trace2
fig.add_trace(go.Scatter(x=Y_axered , y=X_axe, line = dict(color = '#666768', width=0.5), fill='tonexty',fillcolor='#eaf286', mode='lines',showlegend=False), row=1, col=1)# fill area between trace0 and trace3
fig.add_trace(go.Scatter(x=Y_axe    , y=X_axe, line = dict(color = '#666768', width=0.5), mode='lines',line_color='black',showlegend=False), row=1, col=1)  #trace4


fig.add_trace(go.Scatter(x=Y_axe, y=X_axe, line = dict(color = '#4eb3f9', width=1),showlegend=False), row=1, col=2)
fig.add_trace(go.Scatter(x=Y_axe, y=X_axe, line = dict(color = '#7000ff', width=1),showlegend=False), row=1, col=3)
fig.update_xaxes(title_text="AMA1", row=1, col=1)
fig.update_xaxes(title_text="AMA3", row=1, col=2)
fig.update_xaxes(title_text="AMA5", row=1, col=3)
fig.update_yaxes(title_text="Depth", row=1, col=1, autorange='reversed')
fig.update_layout(title_text="Log Plots", height=900)
fig.update_layout(title_text="Well Log Plot", height=900)
fig.update_layout(xaxis2= {'anchor': 'y2', 'overlaying': 'x', 'side': 'top'},
                  yaxis_domain=[0.0, 1]);
fig.show()
Layout({
    'template': '...',
    'xaxis': {'anchor': 'y', 'domain': [0.0, 0.2888888888888889]},
    'xaxis2': {'anchor': 'y2', 'domain': [0.35555555555555557, 0.6444444444444445]},
    'xaxis3': {'anchor': 'y3', 'domain': [0.7111111111111111, 1.0]},
    'yaxis': {'anchor': 'x', 'domain': [0.0, 1.0]},
    'yaxis2': {'anchor': 'x2', 'domain': [0.0, 1.0], 'matches': 'y', 'showticklabels': False},
    'yaxis3': {'anchor': 'x3', 'domain': [0.0, 1.0], 'matches': 'y', 'showticklabels': False}
})

and the result is

Hi,

The reason why the 2nd plot disappears is that xaxis2 refers to the axis on the second plot, which you moved to the top of the first plot. My guess is that plotly removes “automatically” plots with missing x-y axis.

There might be other ways to do this, but I have used this approach in the past successfully and want to share with you. I adapted this approach from this amazing answer from @vestland, Plotly :goat: on SO.

The idea is to create one “ghost” trace for each subplot and assign a new x-axis to each. Then you can move the original axis to the top and each new axis to the bottom (they are not shown by default). Finally, you can tweak a bit the y domain of each subplot to make some space for the bottom.

Here’s a simple example using two subplots:

df = px.data.iris()
fig = make_subplots(rows=1, cols=2,  shared_yaxes=True)

fig.add_trace(go.Scatter(x=df.sepal_width, y=df.sepal_length, name='sepal_length'), row=1, col=1)
fig.add_trace(go.Scatter(x=df.sepal_width, y=df.sepal_length, name='sepal_length'), row=1, col=2)

# Create two "ghost traces"...
fig.add_trace(go.Scatter(x=df.sepal_width, y=np.nan*df.sepal_width, showlegend=False), row=1, col=1)
fig.add_trace(go.Scatter(x=df.sepal_width, y=np.nan*df.sepal_width, showlegend=False), row=1, col=2)

# ...and assign them to new axis objects.
fig.data[1].xaxis = 'x3' 
fig.data[3].xaxis = 'x4'

# This will:
# 1. move the original xaxis to the top
# 2. move the new xaxis to the bottom in the same position as the original xaxis
# 3. adjust the y domain to "give room" for the ticklabels
fig.update_layout(
    xaxis1={'side': 'top'}, 
    xaxis2={'side': 'top'},
    xaxis3={'anchor': 'free', 'position': 0, 'overlaying': 'x1'},
    xaxis4={'anchor': 'free', 'position': 0, 'overlaying': 'x2'},
    yaxis1={'domain': [0.05, 1]},
    yaxis2={'domain': [0.05, 1]},
)

You might have to tweak a bit the spacing if you are adding titles for the axis and so on.

1 Like

Hey @smail ,

I read @adamschroeder’s and @jlfsjunior’s answers and tried to create an example.

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

data = pd.read_csv("example.dat", sep = "\t")



# SUBPLOT CONFIG
fig = make_subplots(rows=1,
                    cols=3,

                    specs=[[{"type": "scatter"},
                            {"type": "scatter"},
                            {"type": "scatter"}
                            ]
                           ],

                    column_widths=[2.5, 2.5, 2.5],
                    shared_yaxes=True
                    )



# GHOST TRACES TRACE NAMES: ghost1 and ghost2
fig.add_trace(go.Scatter(x=None, y=None, name="ghost1", showlegend=False), row=1, col=1)    #COLUMN 1
fig.add_trace(go.Scatter(x=None, y=None, name="ghost2", showlegend=False), row=1, col=2)    #COLUMN 2

#Since we have 3 subplots
#x-axis ->     xaxis, xaxis2, xaxis3

#When we add additional traces (2 ghost + 1 data)
#New x-axis -> xaxis4, xaxis5, xaxis6


fig.update_layout(xaxis4 = {'anchor': 'y',  'overlaying': 'x', 'side': 'top'})  #Ghost Axis Column1

fig.update_layout(xaxis5 = {'anchor': 'y2', 'overlaying': 'x2', 'side': 'top'}) #Ghost Axis Column2

fig.update_layout(xaxis6 = {'anchor': 'y3', 'overlaying': 'x3', 'side': 'top'}) #Data Axis Column3
    


# ADD TRACES

# ONLY TEMP (FIRST GRAPH)
fig.add_trace(go.Scatter(x=data["temperature"],
                         y=data["depth(m)"]*-1,

                         name="temperature",
                        
                         mode='lines',
                         marker=dict(color="red"),
                       
                         ),
              
              row=1, col=1
              )

# ONLY PRESSURE-(PSIG) (SECOND GRAPH)
fig.add_trace(go.Scatter(x=data["pressure(psig)"],
                         y=data["depth(m)"]*-1,

                         name="pressure(psig)",
                        
                         mode='lines',
                         marker=dict(color="blue"),
                       
                         ),
              
              row=1, col=2
              )


# TEMP + PRESSURE-(PSİG) (THIRD GRAPH)
fig.add_trace(go.Scatter(x=data["pressure(psig)"],
                         y=data["depth(m)"]*-1,

                         name="pressure_temp",
                        
                         mode='lines',
                         marker=dict(color="green"),
                         
                       
                         ),
              
              row=1, col=3
              )

fig.add_trace(go.Scatter(x=data["temperature"],
                         y=data["depth(m)"]*-1,

                         name="temp_pressure",
                        
                         mode='lines',
                         marker=dict(color="purple"),
                       
                         ),
              
              row=1, col=3
              )



# UPDATE AXİS

#Ghost Traces (Column 1 and 2)
fig.update_traces(xaxis='x4',yaxis='y',   selector = ({'name':'ghost1'}))
fig.update_traces(xaxis='x5',yaxis='y2', selector = ({'name':'ghost2'}))

#Set Range
fig.update_layout(xaxis4_range=[data["temperature"].min(),data["temperature"].max()])
fig.update_layout(xaxis5_range=[data["pressure(psig)"].min(),data["pressure(psig)"].max()])


#Real Data (Column 3)
fig.update_traces(xaxis='x3',yaxis='y3', selector = ({'name':'pressure_temp'}))
fig.update_traces(xaxis='x6',yaxis='y3', selector = ({'name':'temp_pressure'}))

print(fig.layout)

fig.show()


Feedbacks are welcome : )

3 Likes

Now I get an amazing plot :slight_smile:

Many thanks to you all.

fig = make_subplots(rows=1,cols=3,
                    specs=[[{"type": "scatter"},
                            {"type": "scatter"},
                            {"type": "scatter"}
                            ]
                           ],
                    column_widths=[2.5, 2.5, 2.5],
                    shared_yaxes=True, shared_xaxes=True 
                    )

fig.add_trace(go.Scatter(x=None, y=None, name="ghost1", showlegend=False), row=1, col=1)   
fig.add_trace(go.Scatter(x=None, y=None, name="ghost2", showlegend=False), row=1, col=2)   
fig.add_trace(go.Scatter(x=None, y=None, name="ghost3", showlegend=False), row=1, col=3)   


fig.update_layout(xaxis4 = {'anchor': 'y',  'overlaying': 'x', 'side': 'top'})  
fig.update_layout(xaxis5 = {'anchor': 'y2', 'overlaying': 'x2', 'side': 'top'}) 
fig.update_layout(xaxis6 = {'anchor': 'y3', 'overlaying': 'x3', 'side': 'top'}) 


fig.add_trace(go.Scatter(x=Y_axeline, y=X_axe, line = dict(color = '#666768', width=0.5), hoverinfo='none', fill=None, mode='lines',line_color='black', showlegend=False), row=1, col=1)  #trace0
fig.add_trace(go.Scatter(x=Y_axegrn , y=X_axe, line = dict(color = '#666768', width=0.5), hoverinfo='none', fill='tonexty', fillcolor='rgba(0,250,0,0.4)', mode='lines', showlegend=False), row=1, col=1) # fill area between trace0 and trace1
fig.add_trace(go.Scatter(x=Y_axeline, y=X_axe, line = dict(color = '#666768', width=0.5), hoverinfo='none', fill=None, mode='lines', line_color='black',showlegend=False ), row=1, col=1) #trace2
fig.add_trace(go.Scatter(x=Y_axered , y=X_axe, line = dict(color = '#666768', width=0.5), hoverinfo='none', fill='tonexty',fillcolor='#eaf286', mode='lines',showlegend=False), row=1, col=1)# fill area between trace0 and trace3
fig.add_trace(go.Scatter(x=Y_axe    , y=X_axe, line = dict(color = '#666768', width=0.5),
  text ="x+y",hoverinfo = "x+y", mode='lines',line_color='black',showlegend=False), row=1, col=1)  #trace4

if threshold==threshold:
  fig.add_trace(go.Scatter(x=Y_axe, y=X_axe, line = dict(color = '#4eb3f9', width=1),hoverinfo = "x+y",showlegend=False), row=1, col=2)
  fig.update_xaxes(title_text="AMA3", row=1, col=2)
if threshold==threshold:
  fig.add_trace(go.Scatter(x=Y_axe, y=X_axe, line = dict(color = '#7000ff', width=1),hoverinfo = "x+y",showlegend=False), row=1, col=3)
  fig.update_xaxes(title_text="AMA5", row=1, col=3)

fig.update_traces(xaxis='x4',yaxis='y' , selector = ({'name':'ghost1'}))
fig.update_traces(xaxis='x5',yaxis='y2', selector = ({'name':'ghost2'}))

fig.update_layout(xaxis4_range=[Y_axe.min(),Y_axe.max()])
fig.update_layout(xaxis5_range=[Y_axe.min(),Y_axe.max()])
fig.update_layout(xaxis6_range=[Y_axe.min(),Y_axe.max()])


fig.update_xaxes(title_text="AMA1", row=1, col=1)
fig.update_xaxes(tickangle=0, tickfont=dict(family='Rockwell', color='crimson', size=14))
fig.update_yaxes(title_text="Depth", row=1, col=1, autorange='reversed', tickangle=0, tickfont=dict(family='Rockwell', color='crimson', size=14))
fig.update_layout(title_text="Log Plots", height=900)
fig.update_layout(title_text="Well Log Plot", height=900)

print(fig.layout)
fig.show()

1 Like