检测异常值可以在 DBSCAN R 包的帮助下完成,DBSCAN R 包是用于集群识别的著名算法(有关更多详细信息,请参阅WIKIPEDIA)。
这个函数有三个重要的输入:
- x:您的数据(仅数值)
- eps:目标最大距离
- minPts:将它们视为集群的最小点数
评估 eps 可以在 knndist(...) 和 knndistplot(...) 函数的帮助下完成:
- knndistplot 将为给定的 k(即 minPts)绘制数据集上的 eps 值 ==> 您可以直观地选择一个有效的 eps 值(通常在膝盖曲线部分)
- knndist 将评估 eps 值并将它们从矩阵中返回。k 输入将生成 1:1:k 估值,您可以使用结果以编程方式确定准确的 eps 和 k 值
接下来,您只需使用 dbscan(yourdata, eps, k) 来获取具有以下组件的 dbscan 对象:
- eps:用于计算的 eps
- minPts:识别一个簇的最小点数
- cluster:一个整数向量,用于标识属于(=1)或不属于(=0)的点。最后一个对应于您要消除的异常值。
请注意 dbscan 的以下限制:
- dbscan 使用欧几里得距离,因此它被提交给“维度诅咒”。这可以通过使用 PCA 来避免
- dbscan 消除了可能产生未知点的叠加点。这可以通过使用左外连接将结果与数据合并或使用 jitter(...) 函数来解决,该函数会为数据添加噪声。根据您显示的数据,我认为您的数据可能是这种情况
知道这个限制,dbscan 包提供了两种替代方法:LOF 和 OPTICS(DBSCAN 的扩展)
编辑于 2016 年 1 月 25 日
在@rawr 回答之后,我将给出一个基于mtcars
数据集的示例,以展示如何dbscan
用于识别异常值。请注意,我的示例将使用优秀的data.table
包而不是经典的data.frame
.
首先,我开始复制 rawr 的方法来说明 data.table 的使用
require(data.table)
require(ggplot2)
require(dbscan)
data(mtcars)
dt_mtcars <- as.data.table(mtcars)
# based on rawr's approach
plot(wt~mpg, data=dt_mtcars)
lo <- loess.smooth(dt_mtcars[,mpg], dt_mtcars[,wt])
lines(lo$x,lo$y, lwd=3)
lines(lo$x,lo$y * 1.2, lwd=3 , col=2 )
lines(lo$x,lo$y / 1.2, lwd=3 , col=2 )

因此,我们可以评估我们是否获得了独立于基础支持的相同结果。
其次,以下代码说明了 DBSCAN 方法,该方法从确定eps
和开始k
,即识别集群的必要点数:
res_knn = kNNdist( dt_mtcars[, .(wt, mpg)] , k = 10)
dim_knn = dim(res_knn)
x_knn = seq(1, dim_knn[1])
ggplot() +
geom_line( aes( x = x_knn , y = sort(res_knn[, 1]) , col = 1 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 2]) , col = 2 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 3]) , col = 3 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 4]) , col = 4 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 5]) , col = 5 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 6]) , col = 6 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 7]) , col = 7 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 8]) , col = 8 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 9]) , col = 9 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 10]) , col = 10 ) ) +
xlab('sorted results') +
ylab('kNN distance')
结果绘制在下图中:

它表明计算出的 kNN 距离对该因子敏感,k
但是分离异常值的准确eps
值位于曲线的膝部 ==> 合适eps
的位于 2 到 4 之间。这是一种视觉评估,可以通过适当的自动化搜索算法(例如,请参阅此链接)。关于k
,必须定义权衡,知道 k 越低,结果越不严格。
eps = 3
在下一部分中,我们将使用(基于视觉估计)对dbscan 进行参数化,并k = 4
获得稍微严格的结果。我们将借助 rawr 的代码绘制这些结果:
eps = 3
k = 4
res_dbscan = dbscan( dt_mtcars[, .(wt, mpg)] , eps , k )
plot(wt~mpg, data=dt_mtcars, col = res_dbscan$cluster)
lo <- loess.smooth(dt_mtcars[res_dbscan$cluster>0,mpg], dt_mtcars[res_dbscan$cluster>0,wt])
lines(lo$x,lo$y, lwd=3)
lines(lo$x,lo$y * 1.2, lwd=3 , col=2 )
lines(lo$x,lo$y / 1.2, lwd=3 , col=2 )

我们得到这个数字,我们可以评估我们从 rawr 的方法得到不同的结果,其中位于的点mpg = [10,13]
被视为异常值。
与 rawr 的解决方案相比,这些结果可能被认为是奇怪的,后者在具有双变量数据 (Y ~ X) 的假设下工作。然而mtcars
,变量之间的关系可能是(或不是)线性的多维数据集......为了评估这一点,我们可以散点图这个数据集,例如过滤数值
pairs(dt_mtcars[, .(mpg, disp, hp, drat, wt, qsec)])

如果我们只关注结果wt ~ mpg
,我们乍一看可能会认为它是一种反线性关系。但是对于其他绘制的关系,情况可能并非如此,并且在 N-Dim 环境中查找异常值有点棘手。实际上,当在特定的 2D 比较中投影时,一个点可能会被视为异常值……但如果我们添加一个新的比较维度,则相反。实际上,我们可能具有可以识别的共线性,从而加强或不加强集群关系。
我的朋友们,我同意它很多,if
为了说明这种情况,我们将继续对 的dbscan
数值进行分析mtcars
。
所以我将复制前面介绍的过程,让我们从 kNN 距离分析开始:
res_knn = kNNdist( dt_mtcars[, .(mpg, disp, hp, drat, wt, qsec)] , k = 10)
dim_knn = dim(res_knn)
x_knn = seq(1, dim_knn[1])
ggplot() +
geom_line( aes( x = x_knn , y = sort(res_knn[, 1]) , col = 1 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 2]) , col = 2 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 3]) , col = 3 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 4]) , col = 4 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 5]) , col = 5 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 6]) , col = 6 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 7]) , col = 7 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 8]) , col = 8 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 9]) , col = 9 ) ) +
geom_line( aes( x = x_knn , y = sort(res_knn[, 10]) , col = 10 ) ) +
xlab('sorted results') +
ylab('kNN distance')

与 上产生的分析相比wt ~ mpg
,我们可以看到kNNdist(...)
产生了更重要的 kNN 距离(k = 10
例如,直到 200)。然而,我们仍然有膝盖部分,它可以帮助我们估计一个合适的eps
值。
在下一部分中,我们将使用eps = 75
andk = 5
和
# optimal eps value is between 40 (k=1) and 130 (k=10)
eps = 75
k = 5
res_dbscan = dbscan( dt_mtcars[, .(mpg, disp, hp, drat, wt, qsec)] , eps , k )
pairs(dt_mtcars[, .(mpg, disp, hp, drat, wt, qsec)] , col = res_dbscan$cluster+2L)

因此,该分析的散点图突出表明,由于变量之间的复杂关系,在 N-Dim 环境中识别异常值可能很棘手。但请注意,在大多数情况下,异常值位于 2D 投影的角落部分,这加强了用wt ~ mpg