Search code examples
pythonplotlyplotly-python

Adding Vertical Rectangles to Plotly Subplots Without Using Plotly Express


I'm trying to create shaded areas that correspond to different date ranges in a plotly chart that has subplots.

Ideally I'd like for each shaded rectangle to be suitably fitted to each subplot, but I'm finding this difficult. Here's some sample code:

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

df = pd.DataFrame({'A': list(range(25)),
                   'B': list(range(25, 50)),
                   'C': list(range(50, 75))}, index=pd.date_range('20200101', periods=25))

fig = make_subplots(rows=3, cols=1)

for idx, col in enumerate(df.columns):
    fig.add_trace(go.Scatter(x=df.index, y=df[col]), row=idx + 1, col=1)

shape_dict = {'type':'rect', 'xref':'x', 'yref':'paper', 'x0':'2020-01-03', 'x1':'2020-01-12', 'y0':0, 'y1':1, 'fillcolor': 'LightSalmon', 'layer': 'below', 'opacity': 0.25, 'line_width': 0}

If I do fig.update_layout(shapes=[shape_dict]), then I get this:

enter image description here

Not too bad, but I'd prefer to have each of these shapes fitted separately into their own subplot.

When I try doing this with add_shape, the shaded area loses its scaling:

for idx, col in enumerate(df.columns):
    fig.add_trace(go.Scatter(x=df.index, y=df[col]), row=idx + 1, col=1)
    fig.add_shape(shape_dict, row=idx + 1, col=1)

And that gives me this:

enter image description here

I would prefer not to have to re-calculate axes individually.

I also can't access add_vrect -- I'm not sure why, but it's not available as a method, and I also can't use plotly.express, and most of the plot.ly's documentation uses the px charts and their methods to do what I'm describing.

EDIT

To respond to the answer below, add_vrect does not work on my version of plotly, which is 4.12.0.

For example the sample code in r-beginners returns me this:

df = pd.DataFrame({'A': list(range(25)),
                   'B': list(range(25, 50)),
                   'C': list(range(50, 75))}, index=pd.date_range('20200101', periods=25))

fig = make_subplots(rows=3, cols=1)

for idx, col in enumerate(df.columns):
    fig.add_trace(go.Scatter(x=df.index, y=df[col]), row=idx + 1, col=1)

# shape_dict = {'type':'rect', 'xref':'x', 'yref':'paper', 'x0':'2020-01-03', 'x1':'2020-01-12', 'y0':0, 'y1':1, 'fillcolor': 'LightSalmon', 'layer': 'below', 'opacity': 0.25, 'line_width': 0}
# fig.update_layout(shapes=[shape_dict])

fig.add_vrect(
    x0="2020-01-03", x1="2020-01-12",
    y0=0, y1=1,
    fillcolor="LightSalmon", opacity=0.25,
    layer="below", line_width=0)

fig.show()

Returns the error message: AttributeError: 'Figure' object has no attribute 'add_vrect'


Solution

  • Shading is output to each subplot with add_vrect(). The scale is not affected either. Does this answer meet your question's intent?

    import pandas as pd
    import numpy as np
    import plotly.express as px
    import plotly.graph_objects as go
    from plotly.subplots import make_subplots
    
    df = pd.DataFrame({'A': list(range(25)),
                       'B': list(range(25, 50)),
                       'C': list(range(50, 75))}, index=pd.date_range('20200101', periods=25))
    
    fig = make_subplots(rows=3, cols=1)
    
    for idx, col in enumerate(df.columns):
        fig.add_trace(go.Scatter(x=df.index, y=df[col]), row=idx + 1, col=1)
    
    # shape_dict = {'type':'rect', 'xref':'x', 'yref':'paper', 'x0':'2020-01-03', 'x1':'2020-01-12', 'y0':0, 'y1':1, 'fillcolor': 'LightSalmon', 'layer': 'below', 'opacity': 0.25, 'line_width': 0}
    # fig.update_layout(shapes=[shape_dict])
    
    fig.add_vrect(
        x0="2020-01-03", x1="2020-01-12",
        y0=0, y1=1,
        fillcolor="LightSalmon", opacity=0.25,
        layer="below", line_width=0,
    )
    
    fig.show()
    

    enter image description here