5

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'

Jonathan Bechtel
  • 3,497
  • 4
  • 43
  • 73

3 Answers3

3

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

r-beginners
  • 31,170
  • 3
  • 14
  • 32
  • 1
    I"m using plot.ly version 4.12.0, and I cannot access `add_vrect`. Copying and pasting the code you gave returned an `AttributeError` – Jonathan Bechtel Nov 22 '20 at 03:27
  • My environment is also plotly 4.12.0 and python 3.6.3. I just commented out some of your code and added the `fig.add_vrect()`. What attribute error are you seeing? – r-beginners Nov 22 '20 at 03:34
  • 1
    It said that the `fig` had no attribute `add_vrect` – Jonathan Bechtel Nov 22 '20 at 04:26
  • Have you copied my entire code? Has the library been imported both px and go? – r-beginners Nov 22 '20 at 04:37
  • @r-beginners copying and pasting your code gives: `AttributeError: 'Figure' object has no attribute 'add_vrect'` – lambruscoAcido Mar 05 '21 at 10:44
  • @lambruscoAcido I read the comments and ran it again in my environment. No error was encountered. By the way, I checked plotly version 4.14.3 again. See [here](https://plotly.com/python/horizontal-vertical-shapes/). – r-beginners Mar 05 '21 at 12:27
  • @lambruscoAcido Why would you give it a low reputation just because an error occurred in your environment? You can respond in the comments. Is it to ask for a comment response? That is the wrong response. – r-beginners Mar 05 '21 at 12:38
  • Ok, let's be more precise. I run Python 3.7.7 and Plotly 4.9.0 within Jupyter on a Win10 system. If I paste this code 1:1, it does not work (same as Jonathan B). @r-beginners, thx for your link, which shows this was introduced with 4.12. I will reset the downgrade, but pls mention the v4.12 as min requirement. thx & Sry. – lambruscoAcido Mar 06 '21 at 17:22
  • Updated plotly to 4.14.3 and now it runs. – lambruscoAcido Mar 06 '21 at 18:23
0

I can't comment, so posting this as a (potential) answer.

Are you using this within Jupyter? If so, is the jupyterlab-plotly extension for plotly also updated? If not, this might explain why you can't access the add_vrect method. I think the jupyterlab extensions are mostly responsible for the rendering, but it might still throw an error of not finding the method. Quickest way to check/update is jupyter labextension update --all.

sschafer
  • 49
  • 2
0

I am in plotly 5.10.0. fig.add_vrect does have row & col can specify which subplot to draw.

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

fig = make_subplots(rows=2, cols=1, shared_xaxes=True)
fig.add_trace(go.Scatter(x=[0, 100, 200, 400, 800], y=[1, 100, 400, 800, 1600]), row=1, col=1)
fig.add_vrect(x0=200, x1=400, row=1, col=1, fillcolor="grey", opacity=0.25, line_width=0)

fig.add_trace(go.Scatter(x=[0, 100, 200, 400, 800], y=[1, 100, 200, 400, 800]), row=2, col=1)
fig.add_vrect(x0=100, x1=600, row=2, col=1, fillcolor="grey", opacity=0.25, line_width=0)

fig.show()

See following plot results