Python Seaborn 中小平面网格上的箱线图离群值注释

Boxplot outlier annotation over facet grid in Python Seaborn

我正在用 Python Seaborn 包绘制箱线图。我有包含行和列的小平面网格。我已经能够使用 Seaborn 函数 catplot.

我也想注释异常值。我在 SO 找到了一些很好的例子来注释异常值但没有刻面结构。这就是我挣扎的地方。

这是我得到的(大量借用这个 post:Boxplot : Outliers Labels Python):

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.cbook import boxplot_stats
sns.set_style('darkgrid')

Month = np.repeat(np.arange(1, 11), 10)
Id = np.arange(1, 101)
Value = np.random.randn(100)
Row = ["up", "down"]*50

df = pd.DataFrame({'Value': Value, 'Month': Month, 'Id': Id, 'Row': Row})

g = sns.catplot(data=df, x="Month", y="Value", row="Row", kind="box", height=3, aspect=3)

for name, group in df.groupby(["Month", "Row"]):
    fliers = [y for stat in boxplot_stats(group["Value"]) for y in stat["fliers"]]
    d = group[group["Value"].isin(fliers)]
    
    g.axes.flatten().annotate(d["Id"], xy=(d["Month"] - 1, d["Value"]))

数据框d按补丁收集所有异常值。最后一行旨在将 d 与图 g 补丁相匹配。但是,这不起作用,但我还没有找到一种方法将轴展平到一个列表,其中每个元素都对应于一个分组的数据框元素。

我很高兴听到实现此目的的替代版本。

一种方法:

for name, group in df.groupby(["Month", "Row"]):
    fliers = [y for stat in boxplot_stats(group["Value"]) for y in stat["fliers"]]
    d = group[group["Value"].isin(fliers)]
    for i in range(len(d)):
        ngrid = (0 if d.iloc[i,3]=='up' else 1)
        g.fig.axes[ngrid].annotate(d.iloc[i, 2], xy=(d.iloc[i, 1] - 1, d.iloc[i, 0]))

您可以循环 g.axes_dict 来访问每个轴。

import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
from matplotlib.cbook import boxplot_stats

sns.set_style('darkgrid')

Month = np.repeat(np.arange(1, 11), 10)
Id = np.arange(1, 101)
Value = np.random.randn(100)
Row = ["up", "down"] * 50

df = pd.DataFrame({'Value': Value, 'Month': Month, 'Id': Id, 'Row': Row})

g = sns.catplot(data=df, x="Month", y="Value", row="Row", kind="box", height=3, aspect=3)

for row, ax in g.axes_dict.items():
    for month in np.unique(df["Month"]):
        group = df.loc[(df["Row"] == row) & (df["Month"] == month), :]
        fliers = boxplot_stats(group["Value"])[0]["fliers"]
        if len(fliers) > 0:
            for mon, val, id in zip(group["Month"], group["Value"], group["Id"]):
                if val in fliers:
                    ax.annotate(f' {id}', xy=(mon - 1, val))
plt.tight_layout()
plt.show()