Search code examples
pythonplotlyplotly-dashplotly-python

Horizontal scrollbar overlays middle sub plot of stacked Plotly sub plots


The below code creates stacked Plotly sub plots with a shared x axis.

I would like to add a horizontal scrollbar at the bottom.

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

fig = make_subplots(rows=3, cols=1,
                    shared_xaxes=True,
                    vertical_spacing=0.02)

fig.add_trace(go.Scatter(x=[0, 1, 2, 3], y=[10, 11, 12, 13]),
              row=3, col=1)

fig.add_trace(go.Scatter(x=[0, 1, 2, 3], y=[100, 110, 120, 130]),
              row=2, col=1)

fig.add_trace(go.Scatter(x=[0, 1, 2, 3], y=[1000, 1100, 1200, 1300]),
              row=1, col=1)

fig.update_layout(height=500,xaxis=dict(rangeslider=dict(visible=True), type="linear"))
fig.show()

Unfortunately it's corrupting the first sub plot and overlaying the scrollbar over the middle plot.

How do I fix this?

enter image description here


Solution

  • The thing with make_subplots() is that each subplot has its own x/y axes pair, even with shared_xaxes enabled. In your situation for example, it only adds a matches constraint on xaxis and xaxis2 so that they always match the scale of xaxis3. Also, only xaxis3 is rendered.

    So you would need to add the rangeslider to xaxis3 instead of xaxis in the layout so that it can be properly positioned and margins be pushed for it have enough room :

    fig.update_layout(height=500, xaxis3=dict(rangeslider=dict(visible=True), type="linear"))
    

    But there is still an issue due to having these 3 distinct x axes, the rangeslider only displays traces that share the same x axis, so it will only show the third trace here :

    enter image description here


    To obtain an overview of the three traces in the rangeslider window, you would need to create the stacked subplots "manually", ie. without make_subplots() : basically this amounts to specifying a yaxis reference for each trace and define the corresponding domain in the layout and that's it, adding the rangeslider to xaxis will work fine now since all traces belong to it by default :

    fig = go.Figure()
    
    fig.add_trace(go.Scatter(x=[0, 1, 2, 3], y=[11, 10, 12, 13], yaxis='y'))
    fig.add_trace(go.Scatter(x=[0, 1, 2, 3], y=[120, 110, 130, 100], yaxis='y2'))
    fig.add_trace(go.Scatter(x=[0, 1, 2, 3], y=[1300, 1200, 1000, 1100], yaxis='y3'))
    
    fig.update_layout(
        height=600,
        xaxis=dict(rangeslider=dict(visible=True)),
        yaxis=dict(domain=[0, 0.32]),
        yaxis2=dict(domain=[0.34, 0.66]),
        yaxis3=dict(domain=[0.68, 1])
    )
    
    fig.show()
    

    enter image description here

    See also for reference : Range Slider with Vertically Stacked Subplots.