python plotly graph_objects box marker的属性异常颜色不起作用(可能的错误)

Posted

技术标签:

【中文标题】python plotly graph_objects box marker的属性异常颜色不起作用(可能的错误)【英文标题】:The property outliercolor of python plotly graph_objects box marker is not working (possible bug) 【发布时间】:2021-09-20 18:27:07 【问题描述】:

我想我在 plotly.graph_objects.box Marker 类中发现了一个错误,因为属性 outliercolor 不起作用。我遵循了https://plotly.github.io/plotly.py-docs/generated/plotly.graph_objects.Box.html#plotly.graph_objects.box.Marker.outliercolor 中的参考,但更改异常值颜色不会有任何区别。

这是一个例子:

import numpy as np
import pandas as pd
import plotly.graph_objects as go
from matplotlib.colors import LinearSegmentedColormap, to_hex

df_plot = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
cat_var = "species"
num_var = "petal_length"

lvls = df_plot[cat_var].unique()
n_levels = len(lvls)
cmap = LinearSegmentedColormap.from_list("my_palette", ["#111539", "#97A1D9"])
my_palette = [to_hex(j) for j in  [cmap(i/n_levels) for i in np.array(range(n_levels))]]

boxes = []
for l in range(n_levels):
    boxes += [
        go.Box(
            name = lvls[l],
            y = df_plot.loc[df_plot.loc[:, cat_var] == lvls[l], num_var].values,
            width = 0.4,
            boxpoints = "outliers",
            marker = 
                "outliercolor": "red", ### there may be a plotly.go bug here
                "color": my_palette[l],
                "size": 30,
                "opacity": 0.5
            
        )
    ]
fig = go.Figure(data = boxes)
fig.update_layout(
    font = dict(
        size = 18
    ),
    showlegend = False,
    plot_bgcolor = "white",
    hoverlabel = dict(
        font_size = 18,
        font_family = "Rockwell"
    )
)
fig.show()

【问题讨论】:

【参考方案1】:

这确实似乎是 Plotly 中的一个错误 - 这可以作为错误报告提交给 Plotly 团队。

值得注意的是,将boxpoints = "outliers" 修改为boxpoints = "suspectedoutliers" 会生成不同颜色的标记,因此suspectedoutliers 的行为与预期相同。但是,您不能使用suspectedoutliers 代替outliers,因为可疑异常值只是所有异常值的一个子集。

您可以通过手动绘制异常值来实现所需的行为。为此,您仍需设置boxpoints=outliers,但随后将异常值绘制为具有所需颜色的单个散点,并覆盖 Plotly 生成的异常值。

这有点密集,因为当 Plotly 库执行此计算时,这需要重写算法以确定异常值准确。不幸的是,您无法以任何方式从 go.Box 或 Plotly as these computations are performed by the javascript under the hood when the figure renders 中提取 Q1、Q3 或其他统计数据。

首先要注意的是,不同 Python 库之间计算 Q1 和 Q3 的方式不同:Plotly 在documentation 中概述了它们的方法,并解释说它们使用Method #10 in this short paper 来计算百分位数。

在 Python 中,使用方法 #10(线性插值)计算百分位数的函数如下所示:

## calculate quartiles as outlined in the plotly documentation 
def get_percentile(data, p):
    data.sort()
    n = len(data)
    x = n*p + 0.5
    x1, x2 = floor(n*p), ceil(n*p)
    y1, y2 = data[x1-1], data[x2-1] # account for zero-indexing
    return y1 + ((x - x1) / (x2 - x1))*(y2 - y1)

现在要从数据集中提取异常值,您需要对数据进行子集化:低于 (Q1 - 1.5 * IQR) 或高于 (Q3 + 1.5 * IQR) 且 IQR = Q3 - Q1 的任何值都被视为异常值。

把这一切放在一起:

from math import floor, ceil
import numpy as np
import pandas as pd
import plotly.graph_objects as go
from matplotlib.colors import LinearSegmentedColormap, to_hex

df_plot = pd.read_csv('https://raw.githubusercontent.com/mwaskom/seaborn-data/master/iris.csv')
cat_var = "species"
num_var = "petal_length"

lvls = df_plot[cat_var].unique()
n_levels = len(lvls)
cmap = LinearSegmentedColormap.from_list("my_palette", ["#111539", "#97A1D9"])
my_palette = [to_hex(j) for j in  [cmap(i/n_levels) for i in np.array(range(n_levels))]]

## calculate quartiles as outlined in the plotly documentation 
def get_percentile(data, p):
    data.sort()
    n = len(data)
    x = n*p + 0.5
    x1, x2 = floor(n*p), ceil(n*p)
    y1, y2 = data[x1-1], data[x2-1] # account for zero-indexing
    return y1 + ((x - x1) / (x2 - x1))*(y2 - y1)

def get_fences(data):
    q1, q3 = get_percentile(data, 0.25), get_percentile(data, 0.75)
    iqr = q3-q1
    return (q1 - (1.5*iqr), q3 + (1.5*iqr))

boxes = []
for l in range(n_levels):
    data = df_plot.loc[df_plot.loc[:, cat_var] == lvls[l], num_var].values
    outliers = data[(data < get_fences(data)[0]) | (data > get_fences(data)[1])]
    print(outliers)
    boxes += [
        go.Box(
            name = lvls[l],
            y = data,
            width = 0.4,
            boxpoints = "outliers",
            marker = 
                "outliercolor": "red", ### there may be a plotly.go bug here
                "color": my_palette[l],
                "size": 30,
                "opacity": 0.5
            
        ),
        go.Scatter(
            x = [lvls[l]]*len("outliers"),
            y = outliers,
            mode = 'markers',
            marker=dict(color="red", size=28, opacity=0.5)
        )
    ]
fig = go.Figure(data = boxes)
fig.update_layout(
    font = dict(
        size = 18
    ),
    showlegend = False,
    plot_bgcolor = "white",
    hoverlabel = dict(
        font_size = 18,
        font_family = "Rockwell"
    )
)
fig.show()

作为检查我们工作的一种方式,您会注意到手动添加的稍小的异常值与 Plotly 确定的异常值相匹配。 (您可以使手动添加的异常值更大,以掩盖 Plotly 生成的不是所需颜色的异常值)

【讨论】:

很有趣,我刚刚发现了“可疑异常值”的这种行为。我有点适应这种像面具一样绘制散点图的方式,它确实有效,但如果 Plotly 本身解决了这个问题,我将不胜感激。但无论如何,我会使用这个想法。谢谢

以上是关于python plotly graph_objects box marker的属性异常颜色不起作用(可能的错误)的主要内容,如果未能解决你的问题,请参考以下文章

python使用matplotlib绘制一条正弦曲线(plot函数可视化sine plot)

plot bar chart using python

Python的 plot函数和绘图参数设置

Python将瀑布图转换为plotly

Python使用plotly绘制数据图表的方法

如何使用 plotly 自定义平行类别图颜色? Python版