情节:如何制作具有多条线和标准偏差阴影区域的图形?

Plotly: How to make a figure with multiple lines and shaded area for standard deviations?

如何使用 Plotly 生成具有阴影标准差的线图?我正在尝试实现类似于 seaborn.tsplot 的东西。任何帮助表示赞赏。

我想出了类似的东西。我 post 此处的代码供其他人使用或提出任何改进建议。

import matplotlib
import random
import plotly.graph_objects as go
import numpy as np


#random color generation in plotly
hex_colors_dic = {}
rgb_colors_dic = {}
hex_colors_only = []
for name, hex in matplotlib.colors.cnames.items():
    hex_colors_only.append(hex)
    hex_colors_dic[name] = hex
    rgb_colors_dic[name] = matplotlib.colors.to_rgb(hex)

data = [[1, 3, 5, 4],
        [2, 3, 5, 4],
        [1, 1, 4, 5],
        [2, 3, 5, 4]]
#calculating mean and standard deviation
mean=np.mean(data,axis=0)
std=np.std(data,axis=0)

#draw figure
fig = go.Figure()
c = random.choice(hex_colors_only)
fig.add_trace(go.Scatter(x=np.arange(4), y=mean+std,
                                     mode='lines',
                                     line=dict(color=c,width =0.1),
                                     name='upper bound'))
fig.add_trace(go.Scatter(x=np.arange(4), y=mean,
                         mode='lines',
                         line=dict(color=c),
                         fill='tonexty',
                         name='mean'))
fig.add_trace(go.Scatter(x=np.arange(4), y=mean-std,
                         mode='lines',
                         line=dict(color=c, width =0.1),
                         fill='tonexty',
                         name='lower bound'))
fig.show()

以下方法对于 pandas 数据框中的列数非常灵活,并使用 。如果行数超过颜色数,则从头开始重新使用颜色。截至目前,px.colors.qualitative.Plotly 可以替换为您可以使用 px.colors.qualitative:

找到的任何十六进制颜色序列
Alphabet = ['#AA0DFE', '#3283FE', '#85660D', '#782AB6', '#565656', '#1...
Alphabet_r = ['#FA0087', '#FBE426', '#B00068', '#FC1CBF', '#C075A6', '...
[...]

完整代码:

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

# sample data in a pandas dataframe
np.random.seed(1)
df=pd.DataFrame(dict(A=np.random.uniform(low=-1, high=2, size=25).tolist(),
                    B=np.random.uniform(low=-4, high=3, size=25).tolist(),
                    C=np.random.uniform(low=-1, high=3, size=25).tolist(),
                    ))
df = df.cumsum()

# define colors as a list 
colors = px.colors.qualitative.Plotly

# convert plotly hex colors to rgba to enable transparency adjustments
def hex_rgba(hex, transparency):
    col_hex = hex.lstrip('#')
    col_rgb = list(int(col_hex[i:i+2], 16) for i in (0, 2, 4))
    col_rgb.extend([transparency])
    areacol = tuple(col_rgb)
    return areacol

rgba = [hex_rgba(c, transparency=0.2) for c in colors]
colCycle = ['rgba'+str(elem) for elem in rgba]

# Make sure the colors run in cycles if there are more lines than colors
def next_col(cols):
    while True:
        for col in cols:
            yield col
line_color=next_col(cols=colCycle)

# plotly  figure
fig = go.Figure()

# add line and shaded area for each series and standards deviation
for i, col in enumerate(df):
    new_col = next(line_color)
    x = list(df.index.values+1)
    y1 = df[col]
    y1_upper = [(y + np.std(df[col])) for y in df[col]]
    y1_lower = [(y - np.std(df[col])) for y in df[col]]
    y1_lower = y1_lower[::-1]

    # standard deviation area
    fig.add_traces(go.Scatter(x=x+x[::-1],
                                y=y1_upper+y1_lower,
                                fill='tozerox',
                                fillcolor=new_col,
                                line=dict(color='rgba(255,255,255,0)'),
                                showlegend=False,
                                name=col))

    # line trace
    fig.add_traces(go.Scatter(x=x,
                              y=y1,
                              line=dict(color=new_col, width=2.5),
                              mode='lines',
                              name=col)
                                )
# set x-axis
fig.update_layout(xaxis=dict(range=[1,len(df)]))

fig.show()

我写了一个函数来扩展 plotly.express.line 与 Plotly Express 相同的高级接口。 line 函数(下面的源代码)以与 plotly.express.line 完全相同的方式使用,但允许使用标志参数 error_y_mode 的连续误差带,它可以是 'band''bar'。在第二种情况下,它产生与原始 plotly.express.line 相同的结果。这是一个用法示例:

import plotly.express as px

df = px.data.gapminder().query('continent=="Americas"')
df = df[df['country'].isin({'Argentina','Brazil','Colombia'})]
df['lifeExp std'] = df['lifeExp']*.1 # Invent some error data...

for error_y_mode in {'band', 'bar'}:
    fig = line(
        data_frame = df,
        x = 'year',
        y = 'lifeExp',
        error_y = 'lifeExp std',
        error_y_mode = error_y_mode, # Here you say `band` or `bar`.
        color = 'country',
        title = f'Using error {error_y_mode}',
        markers = '.',
    )
    fig.show()

生成以下两个图:

扩展plotly.express.lineline函数的源代码是这样的:

import plotly.express as px
import plotly.graph_objs as go

def line(error_y_mode=None, **kwargs):
    """Extension of `plotly.express.line` to use error bands."""
    ERROR_MODES = {'bar','band','bars','bands',None}
    if error_y_mode not in ERROR_MODES:
        raise ValueError(f"'error_y_mode' must be one of {ERROR_MODES}, received {repr(error_y_mode)}.")
    if error_y_mode in {'bar','bars',None}:
        fig = px.line(**kwargs)
    elif error_y_mode in {'band','bands'}:
        if 'error_y' not in kwargs:
            raise ValueError(f"If you provide argument 'error_y_mode' you must also provide 'error_y'.")
        figure_with_error_bars = px.line(**kwargs)
        fig = px.line(**{arg: val for arg,val in kwargs.items() if arg != 'error_y'})
        for data in figure_with_error_bars.data:
            x = list(data['x'])
            y_upper = list(data['y'] + data['error_y']['array'])
            y_lower = list(data['y'] - data['error_y']['array'] if data['error_y']['arrayminus'] is None else data['y'] - data['error_y']['arrayminus'])
            color = f"rgba({tuple(int(data['line']['color'].lstrip('#')[i:i+2], 16) for i in (0, 2, 4))},.3)".replace('((','(').replace('),',',').replace(' ','')
            fig.add_trace(
                go.Scatter(
                    x = x+x[::-1],
                    y = y_upper+y_lower[::-1],
                    fill = 'toself',
                    fillcolor = color,
                    line = dict(
                        color = 'rgba(255,255,255,0)'
                    ),
                    hoverinfo = "skip",
                    showlegend = False,
                    legendgroup = data['legendgroup'],
                    xaxis = data['xaxis'],
                    yaxis = data['yaxis'],
                )
            )
        # Reorder data as said here: 
        reordered_data = []
        for i in range(int(len(fig.data)/2)):
            reordered_data.append(fig.data[i+int(len(fig.data)/2)])
            reordered_data.append(fig.data[i])
        fig.data = tuple(reordered_data)
    return fig

其他人发布的很棒的自定义回复。如果有人对 plotly 官方网站的代码感兴趣,请参阅此处:https://plotly.com/python/continuous-error-bars/