我在评论中建议,这种情况下的“异常值”可能被定义为以“极端”值为中心的“小”集群的成员。 引用术语的含义需要量化,但显然它们可以是:“小”将是少于 10 个值的集群,“极端”可以确定为相对于混合模型中的组件均值集的离群值。 在这种情况下,可以通过对任何合理的数据聚类分析进行简单的后处理来找到异常值。
在微调这种方法时必须做出选择。这些选择将取决于数据的性质,因此不能在这样的一般答案中完全指定。相反,让我们分析一些数据。 我使用R它是因为它在这个网站上的流行和简洁(甚至与 Python 相比)。
首先,按照问题中的描述创建一些数据:
set.seed(17) # For reproducible results
centers <- rnorm(100, mean=100, sd=20)
x <- c(centers + rnorm(100*100, mean=0, sd=1),
rnorm(100, mean=250, sd=1),
rnorm(9, mean=300, sd=1))
该命令指定了 102 个组件:其中 100 个位于正态 (100, 20) 分布中的 100 个独立抽取(因此往往位于 50 到 150 之间);其中一个以 250 为中心,一个以 300 为中心。然后,它独立于每个组件绘制 100 个值(使用 1 的常见标准偏差),但在以 300 为中心的最后一个组件中,它仅绘制 9 个值。根据异常值的表征,以 250 为中心的 100 个值并不构成异常值:它们应该被视为混合物的一个组成部分,尽管它们的位置远离其他值。然而,一组九个高值完全由异常值组成。 我们需要检测这些,但不需要检测其他。
大多数综合单变量异常值检测程序要么不会检测到这 109 个最高值中的任何一个,要么会指示所有 109 个都是异常值。
假设我们对组件的标准偏差有很好的了解(从先验信息或从探索数据中获得)。使用它来构造混合物的核密度估计:
d <- density(x, bw=1, n=1000)
plot(d, main="Kernel density")

最右边的(几乎不可见的)blip 有资格作为一组异常值:它的小区域(小于总数的 10/10109 = 0.001)表明它仅包含几个值,并且它的情况位于 x- 的一个极端轴为其赢得了“异常值”而不是“内部值”的称号。检查这些事情很简单:
x0 <- d$x[d$y > 1000/length(x) * dnorm(5)]
gaps <- tail(x0, -1) - head(x0, -1)
histogram(gaps, main="Gap Counts")

密度估计d由 1000 个 bin 的一维网格表示。这些命令保留了所有密度足够大的 bin。对于“大”,我选择了一个非常小的值,以确保即使单个孤立值的密度也被拾取,但不会太小以至于明显分离的组件被合并。
显然,间隙分布有两个高异常值(可以使用任何简单的程序自动检测到,甚至是临时程序)。一个特征是它们都超过 25(在这个例子中)。让我们找到与它们关联的值:
large.gaps <- gaps > 25
ranges <- rbind(tail(x0,-1)[large.gaps], c(tail(head(x0,-1)[large.gaps], -1), max(x))
输出是
[,1] [,2]
[1,] 243.9937 295.7732
[2,] 256.3758 300.9340
在数据范围内(从 25 到 301),这些差距决定了两个潜在的异常范围,一个从 244 到 256(第 1 列),另一个从 296 到 301(第 2 列)。让我们看看有多少值位于这些范围内:
lapply(apply(ranges, 2, function(r){x[r[1] <= x & x <= r[2]]}), length)
结果是
[[1]]
[1] 100
[[2]]
[1] 9
100 太大了以至于不寻常:这是混合物的成分之一。但是9已经足够小了。仍有待观察这些组件中的任何一个是否可能被认为是异常的(而不是内部的):
apply(ranges, 2, mean)
结果是
[1] 250.1848 298.3536
100 点聚类的中心在 250,9 点聚类的中心在 298,与其余数据的距离足以构成异常值聚类。 我们得出结论有九个异常值。具体来说,这些是由 的第 2 列确定的值ranges,
x[ranges[1,2] <= x & x <= ranges[2,2]]
按顺序,它们是
299.0379 300.0376 300.2696 300.3892 300.4250 300.5659 300.7018 300.8436 300.9340