Plotly:如何根据条件为两条线之间的填充着色?

Plotly: How to color the fill between two lines based on a condition?

我想在我的 Plotly 图表上的黑色和蓝色线条之间添加填充颜色。我知道这已经可以通过 Plotly 完成,但我不确定如何根据条件用两种颜色填充图表。

蓝色背景的图表是我的Plotly图表。我想让它看起来像白色背景的图表。 (忽略白色图表上的红色和绿色条)

我希望它通过的条件是:

如果黑线在蓝线上方,则用绿色填充两条线之间的区域。

如果黑线在蓝线下方,则将两条线之间的区域填充为红色。

如何使用 Plotly 完成此操作?如果 Plotly 无法做到这一点,可以使用其他与 Python.

配合使用的绘图工具来完成吗?

出于多种原因(如果您有兴趣,我愿意进一步解释)最好的方法似乎是每次平均值交叉时向 go.Figure() 对象添加两条轨迹,然后使用 fill='tonexty' 为第二条迹线定义填充:

for df in dfs:
    fig.add_traces(go.Scatter(x=df.index, y = df.ma1,
                              line = dict(color='rgba(0,0,0,0)')))
    
    fig.add_traces(go.Scatter(x=df.index, y = df.ma2,
                              line = dict(color='rgba(0,0,0,0)'),
                              fill='tonexty', 
                              fillcolor = fillcol(df['label'].iloc[0])))

fillcolor 是一个简单的自定义函数,在下面的完整代码段中进行了描述。每次您的平均值相互交叉时,我都使用 中描述的方法在数据框中生成必要的拆分。

情节

完整代码:

import plotly.graph_objects as go
import numpy as np

import pandas as pd
from datetime import datetime
pd.options.plotting.backend = "plotly"

# sample data
df = pd.read_csv('https://raw.githubusercontent.com/plotly/datasets/master/finance-charts-apple.csv')
df.index = df.Date
df = df[['AAPL.Close', 'mavg']]
df['mavg2'] = df['AAPL.Close'].rolling(window=50).mean()
df.columns = ['y', 'ma1', 'ma2']
df=df.tail(250).dropna()
df1 = df.copy()

# split data into chunks where averages cross each other
df['label'] = np.where(df['ma1']>df['ma2'], 1, 0)
df['group'] = df['label'].ne(df['label'].shift()).cumsum()
df = df.groupby('group')
dfs = []
for name, data in df:
    dfs.append(data)

# custom function to set fill color
def fillcol(label):
    if label >= 1:
        return 'rgba(0,250,0,0.4)'
    else:
        return 'rgba(250,0,0,0.4)'

fig = go.Figure()

for df in dfs:
    fig.add_traces(go.Scatter(x=df.index, y = df.ma1,
                              line = dict(color='rgba(0,0,0,0)')))
    
    fig.add_traces(go.Scatter(x=df.index, y = df.ma2,
                              line = dict(color='rgba(0,0,0,0)'),
                              fill='tonexty', 
                              fillcolor = fillcol(df['label'].iloc[0])))

# include averages
fig.add_traces(go.Scatter(x=df1.index, y = df1.ma1,
                          line = dict(color = 'blue', width=1)))

fig.add_traces(go.Scatter(x=df1.index, y = df1.ma2,
                          line = dict(color = 'red', width=1)))

# include main time-series
fig.add_traces(go.Scatter(x=df1.index, y = df1.y,
                          line = dict(color = 'black', width=2)))

fig.update_layout(showlegend=False)
fig.show()

[这是问题的javascript解决方案]

我采用了一种完全不同的方法来创建气候图。

我使用了一组函数来检查两条轨迹是否相互相交。对于每个交叉点,该函数将获取轨迹的所有点直到交叉点以创建单独的多边形并将它们着色。 这个函数是递归的。如果没有交集,则只有一个多边形,如果有一个交集,则有两个多边形,如果有两个交集,则有三个多边形,等等。然后将这些多边形添加到图表中。

这里给出plotly中shapes的介绍: https://plotly.com/javascript/shapes/

我使用一个现有的函数从这里找到交点:

我编写了自己的函数来创建多边形字符串,这些多边形字符串创建了多边形所需的路径字符串。根据哪条线在另一条线之上(可以使用简单的比较来实现),变量“颜色”是绿色或红色。

function draw_and_colour_in_polygon(temperature_values, precipitation_values, x_values) {

        var path_string = '';
        for (var i = 0; i < x_values.length; i++) {
            path_string += ' L ' + x_values[i] + ', '+ temperature_values[i];
        }
        for (var i = precipitation_values.length-1; i >= 0; i--) {
            path_string += ' L ' + x_values[i] + ', ' + precipitation_values[i];
        }
        
        path_string += ' Z';
        path_string = path_string.replace(/^.{2}/g, 'M ');
        
        if (temperature_values[0] >= precipitation_values[0] && temperature_values[1] >= precipitation_values[1]) {
            var colour = 'rgba(255, 255, 0, 0.5)';
        }
        else {
            var colour = 'rgba(65, 105, 225, 0.5)';
        }
        
        return {
            path: path_string,
            polygon_colour: colour,
        }; 
};

所有这些放在一起看起来像这样:

在这种情况下,我们将三个单独的多边形添加到图表中。它们是蓝色还是黄色取决于温度值是否高于降水值,反之亦然。请记住,多边形由两条轨迹的 y 值组成。我的两条迹线不使用相同的 y-axis,因此必须对其中一条迹线应用转换函数以协调高度值,然后才能组成多边形字符串。

我无法共享所有代码,因为我还添加了一些位,其中 y-axes 被缩放为更高的值,这会给答案增加相当多的复杂性,但我希望你明白了。