Plotly:如何使用 For 循环突出显示具有矩形形状的 Pandas 时间序列中的某些时期?

NRV*_*RVA 5 python pandas plotly plotly-python

我试图在 Plotly 中突出显示时间段。我似乎最好的方法是使用 Shapes,就像这样,但在现实世界中,您不想像示例 url 中那样手动添加每个形状。我认为 for 循环是最好的解决方案,但欢迎其他(计算量较小的)建议。

我的脚本如下所示:

np.random.seed(12345)
rows = 20
x = pd.Series(np.random.randn(rows),index=pd.date_range('1/1/2020', periods=rows)).cumsum()
df = pd.DataFrame({"index": x})

# add column showing what to shade
df["signal"] = df['index'] < 5

# plot index and highlight periods with Rectangle Shapes in Plotly
fig = px.line(df, x=df.index, y="index")

for row in df.iterrows():
    if df['signal'] == False:
        ply_shapes['shape_' + str(i)]=go.layout.Shape(type="rect",
                                                            x0=df.dato[i-1],
                                                            y0=0,
                                                            x1=df.dato[i],
                                                            y1=2000,
                                                            opacity=0.5,
                                                            layer="below"
                                                        )

lst_shapes=list(ply_shapes.values())
fig.update_layout(shapes=lst_shapes)
fig.show()
Run Code Online (Sandbox Code Playgroud)

但这返回:

ValueError: The truth value of a Series is ambiguous. Use a.empty, a.bool(), a.item(), a.any() or a.all().
Run Code Online (Sandbox Code Playgroud)

ves*_*and 5

您的代码片段无法工作的原因有多种。

1. iterrows()返回一个迭代器,其中包含每行的索引和每行中的数据作为seres。要使用它,您必须替换

for row in df.iterrows():
    if df['signal'] == False:
Run Code Online (Sandbox Code Playgroud)

与(例如):

for row in df.iterrows():
    if row[1]['signal'] == True:
Run Code Online (Sandbox Code Playgroud)

引发您的错误是因为您分配了一系列withTrue, False不是if df['signal'] == False:if row[1]['signal'将做的那样分配单个值。

但仅仅解决这个问题并不能帮助你。至少不在您的代码片段的范围内,因为:

2. dato您的示例数据框中不存在。

类似的问题之前已经被问过并回答过。但由于对于听起来非常不同的问题,解决方案是相似的,因此我决定也为您的用例制定一个自定义解决方案。


在这两种情况下,最好的方法在很大程度上取决于您如何在时间序列中识别和分配突出显示的时间段。以下示例将使用随机数据并根据阈值识别要突出显示的时间段。就像你的问题一样。

我的建议将归结为一个函数,highLights()该函数将一个绘图图、一个 pandas 系列和一个阈值作为输入,以及一些其他细节(看看文档字符串)。

带有一些示例输入的函数highLights()

fig = highLights(fig = fig, variable = 'signal', level = 5, mode = 'above',
               fillcolor = 'rgba(200,0,200,0.2)', layer = 'below')
Run Code Online (Sandbox Code Playgroud)

阴谋

在此输入图像描述

完整代码:

# imports
import numpy as np
import pandas as pd
import plotly.graph_objects as go
import plotly.express as px
import datetime

pd.set_option('display.max_rows', None)

# data sample
cols = ['signal']
nperiods = 200
np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(nperiods, len(cols))),
                  columns=cols)
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),periods=nperiods).tolist()
df['dato'] = datelist 
df = df.set_index(['dato'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum().reset_index()

# plotly setup
fig = px.line(df, x='dato', y=df.columns[1:])
fig.update_xaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,255,0.1)')
fig.update_yaxes(showgrid=True, gridwidth=1, gridcolor='rgba(0,0,255,0.1)')


# function to set background color for a
# specified variable and a specified level
def highLights(fig, variable, level, mode, fillcolor, layer):
    """
    Set a specified color as background for given
    levels of a specified variable using a shape.
    
    Keyword arguments:
    ==================
    fig -- plotly figure
    variable -- column name in a pandas dataframe
    level -- int or float
    mode -- set threshold above or below
    fillcolor -- any color type that plotly can handle
    layer -- position of shape in plotly fiugre, like "below"
    
    """
    
    if mode == 'above':
        m = df[variable].gt(level)
    
    if mode == 'below':
        m = df[variable].lt(level)
        
    df1 = df[m].groupby((~m).cumsum())['dato'].agg(['first','last'])

    for index, row in df1.iterrows():
        #print(row['first'], row['last'])
        fig.add_shape(type="rect",
                        xref="x",
                        yref="paper",
                        x0=row['first'],
                        y0=0,
                        x1=row['last'],
                        y1=1,
                        line=dict(color="rgba(0,0,0,0)",width=3,),
                        fillcolor=fillcolor,
                        layer=layer) 
    return(fig)

fig = highLights(fig = fig, variable = 'signal', level = 5, mode = 'above',
               fillcolor = 'rgba(200,0,200,0.2)', layer = 'below')

fig.update_layout(template = 'plotly_dark')

fig.show()
Run Code Online (Sandbox Code Playgroud)