Plotly:如何在两个垂直线之间设置填充颜色?

7
使用matplotlib,我们可以使用fill_between()轻松地填充两条垂直线之间的区域,如下例所示:

https://matplotlib.org/3.2.1/gallery/lines_bars_and_markers/fill_between_demo.html#selectively-marking-horizontal-regions-across-the-whole-axes

使用matplotlib,我可以做出我需要的东西:

enter image description here

我们有两个信号,我正在计算滚动/移动的Pearson和Spearman相关性。当相关性低于-0.5或高于0.5时,我想给这段时间涂上颜色(Pearson为蓝色,Spearman为橙色)。我还要在所有图表中用灰色加深周末的颜色。
然而,我发现使用Plotly很难实现相同的效果。同时,知道如何在两条水平线之间完成这一操作也将非常有帮助。
请注意,我正在使用Plotly和Dash来加速多个图形的可视化。用户要求更具“动态性”。但是,我不是GUI专家,不能花时间在这上面,尽管我需要提供初始结果。
顺便说一句,我以前尝试过Bokeh,但由于某些原因放弃了。Plotly看起来不错,因为我可以使用Python或R,这是我的主要开发工具。
谢谢,
Carlos
4个回答

7

我认为Plotly没有内置的方法与matplotlib的fill_between() 方法相等。然而,您可以绘制形状,因此一种可能的解决方法是绘制一个灰色的矩形,并设置参数layer="below",以便信号仍然可见。您还可以将矩形的坐标设置在轴范围之外,以确保矩形延伸到绘图的边缘。

通过绘制矩形并以类似的方式设置轴范围,您可以填充水平线之间的区域。

import numpy as np
import plotly.graph_objects as go

x = np.arange(0, 4 * np.pi, 0.01)
y = np.sin(x)

fig = go.Figure()
fig.add_trace(go.Scatter(
    x=x,
    y=y
))

# hard-code the axes
fig.update_xaxes(range=[0, 4 * np.pi])
fig.update_yaxes(range=[-1.2, 1.2])

# specify the corners of the rectangles
fig.update_layout(
    shapes=[
    dict(
        type="rect",
        xref="x",
        yref="y",
        x0="4",
        y0="-1.3",
        x1="5",
        y1="1.3",
        fillcolor="lightgray",
        opacity=0.4,
        line_width=0,
        layer="below"
    ),
    dict(
        type="rect",
        xref="x",
        yref="y",
        x0="9",
        y0="-1.3",
        x1="10",
        y1="1.3",
        fillcolor="lightgray",
        opacity=0.4,
        line_width=0,
        layer="below"
    ),
    ]
)

fig.show()

enter image description here


这个问题可以使用 shapes (type=‘rect’) 来解决吗?(回答很好,只是好奇为什么选择了这种方法)。 - S3DEV
1
哦,那可能更容易,谢谢指出。我更新了我的答案! - Derek O
2
你可以添加 layer="below" 来绘制线条下方的矩形。 - Harm
@Harm te Molder,这是一个好建议,谢谢!我已经更新了我的答案。 - Derek O

6

您没有提供数据样本,我将使用一个合成时间序列来向您展示如何使用自定义函数bgLevel为几个不同类别的定义了起始日期和终止日期的形状添加数字。


两条垂直线之间填充很快就会变成一个矩形。而矩形可以使用fig.add_shape作为形状轻松添加。下面的示例将向您展示如何查找由某些标准给定的时期的开始和结束日期。在您的情况下,这些标准是变量值是否高于或低于某个级别。

使用形状而不是fig.add_trace()中的跟踪,将让您使用layer='below'相对于绘图层定义位置。并且可以使用line=dict(color="rgba(0,0,0,0))轻松隐藏形状轮廓。

图 1: 具有随机数据的时间序列图:

enter image description here

图 2:A > 100时,背景设置为不透明灰色:

enter image description here

图 3:D < 60时,背景也设置为不透明红色

enter image description here

完整代码:

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
nperiods = 200
np.random.seed(123)
df = pd.DataFrame(np.random.randint(-10, 12, size=(nperiods, 4)),
                  columns=list('ABCD'))
datelist = pd.date_range(datetime.datetime(2020, 1, 1).strftime('%Y-%m-%d'),periods=nperiods).tolist()
df['dates'] = datelist 
df = df.set_index(['dates'])
df.index = pd.to_datetime(df.index)
df.iloc[0] = 0
df = df.cumsum().reset_index()

# function to set background color for a
# specified variable and a specified level



# plotly setup
fig = px.line(df, x='dates', 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)')

def bgLevels(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())['dates'].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 = bgLevels(fig = fig, variable = 'A', level = 100, mode = 'above',
               fillcolor = 'rgba(100,100,100,0.2)', layer = 'below')

fig = bgLevels(fig = fig, variable = 'D', level = -60, mode = 'below',
               fillcolor = 'rgba(255,0,0,0.2)', layer = 'below')

fig.show()

谢谢,你的解决方案也可以,虽然在使用子图时会跨越所有图形。但对我来说已经足够好了。 - an_drade
@an_drade 感谢您的反馈!是的,我没有考虑到整个子图的问题。不过也有解决方法。例如,请看这里 - vestland

2

1
这应该是一个被接受的答案。 - Grigorii Smirnov-Pinchukov
确实,这是正确的做法!效果非常好。 - Lucas

0
你可以使用add_vrect()函数。 另外,为了自动化,你可以将句号放入字典中并进行迭代。

enter image description here

import pandas as pd
import numpy as np
import plotly.graph_objects as go

df = pd.DataFrame({
    'DATE': pd.date_range('2023-01-01', '2023-07-31',freq='d')
})
df['VALUE'] = np.random.randint(low=-1, high=2, size=df.shape[0]).cumsum()

dct = {
    'Period 1': {
        'start_date': '2023-01-05',
        'end_date': '2023-02-20',
        'fillcolor': 'rgba(30, 180, 100, 0.2)',
        'linecolor': 'rgba(0,0,0,0)'
    },
    'Period 2': {
        'start_date': '2023-07-01',
        'end_date': '2023-07-16',
        'fillcolor': 'rgba(30, 180, 100, 0.2)',
        'linecolor': 'rgba(0,0,0,0)'
    },
    'Period 3': {
        'start_date': '2023-03-15',
        'end_date': '2023-03-25',
        'fillcolor': 'rgba(195, 105, 10, 0.2)',
        'linecolor': 'rgba(0,0,0,0)'
    },
}

fig = go.Figure()

for k, v in dct.items():
    fig.add_vrect(
        x0=v['start_date'],
        x1=v['end_date'],
        label=dict(
            text=k,
            textposition="top center",
            font=dict(size=20, family="Times New Roman")
        ),
        fillcolor=v['fillcolor'],
        opacity=0.75,
        line_width=0,
    )

fig.update_xaxes(showgrid=False)
fig.add_trace(go.Scatter(x=df['DATE'], y=df['VALUE'], line_color='blue', name='Value'))

fig.show()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接