首页 > 解决方案 > 异常值检测的邻域计算

问题描述

我正在使用 R 编程语言,我正在尝试了解以下用于异常值检测的函数的详细信息: https ://rdrr.io/cran/dbscan/src/R/LOF.R

此函数(来自“dbscan”库)使用局部异常值因子 (LOF) 算法来计算异常值:https ://en.wikipedia.org/wiki/Local_outlier_factor 。

LOF 算法是一种无监督的基于距离的算法,它定义数据集中相对于观测值的“可达性和邻域”的异常值。通常,相对于其附近的其他观测值而言,不是“非常可达”的观测值被认为是“异常值”。基于这些属性(用户指定这些属性,例如邻域(用“k”表示)可以是“3”),该算法为数据集中的每个点分配一个 LOF“分数”。给定观察的 LOF 分数越大,该观察被认为更像是异常值。

现在,我试图更好地理解dbscan::lof()函数中发生的一些计算。

1)基本的LOF算法可以在一些人工创建的数据上运行,如下所示:

```#load library(dbscan)
par(mfrow = c(1,2))
#generate data
n <- 100
x <- cbind(
  x=runif(10, 0, 5) + rnorm(n, sd=0.4),
  y=runif(10, 0, 5) + rnorm(n, sd=0.4)
  )

### calculate LOF score
lof <- lof(x, k=3)

### distribution of outlier factors
summary(lof)
hist(lof, breaks=10)

### point size is proportional to LOF
plot(x, pch = ".", main = "LOF (k=3)")
points(x, cex = (lof-1)*3, pch = 1, col="red") ```

在此处输入图像描述

我的问题是:较大的“k”值是否会导致识别的异常值更少(直方图左偏),但识别出的异常值更“极端”(即更大的 LOF 分数)?

我观察到了这种一般模式,但我不确定这种趋势是否反映在 LOF 算法代码中。例如

#plot LOF results for different values of k

par(mfrow = c(2,2))



### calculate LOF score
lof <- lof(x, k=3)

### distribution of outlier factors
summary(lof)
hist(lof, main = "k = 3",breaks=10)

### calculate LOF score
lof <- lof(x, k=10)

### distribution of outlier factors
summary(lof)
hist(lof, main = "k = 10", breaks=10)

### calculate LOF score
lof <- lof(x, k=20)

### distribution of outlier factors
summary(lof)
hist(lof, main = "k = 20", breaks=10)


### calculate LOF score
lof <- lof(x, k=40)

### distribution of outlier factors
summary(lof)
hist(lof, main = "k = 10", breaks=40)

在此处输入图像描述

在上图中,您可以看到随着“k”值的增加,识别出的异常值越少。这个对吗?

2)是否有一种“最佳”方式为 LOF 算法选择“k”值?看到 LOF 算法如何,在我看来并没有一种“最佳”方式来选择“k”的值。看来您必须参考1)中描述的逻辑:

  1. “k”值越大,识别出的异常值越少,但识别出的异常值更“极端”

  2. 较小的“k”值会导致识别出更多异常值,但识别出的异常值不那么“极端”

这个对吗?

标签: ralgorithmdata-sciencehistogramoutliers

解决方案


关于2),我在这里找到了这个 stackoverflow 帖子:https ://stats.stackexchange.com/questions/138675/choosing-ak-value-for-local-outlier-factor-lof-detection-analysis :

“该论文的作者建议选择一个最小 k 和一个最大 k,并且对于每个点,在该范围内的每个 k 上取最大 LOF 值。他们提供了几个选择边界的指南。”

这是我将上述实现到 R 代码中的逻辑:

library(dbscan)

#generate data
n <- 100
x <- cbind(
  x=runif(10, 0, 5) + rnorm(n, sd=0.4),
  y=runif(10, 0, 5) + rnorm(n, sd=0.4)
  )

x = data.frame(x)

### calculate LOF score for a range of different "k" values:

lof_10 <- lof(x, k=10)
lof_15 <- lof(x, k=15)
lof_20 <- lof(x, k=20)

#append these lof calculations the original data set:

x$lof_10 = lof_10
x$lof_15 = lof_15
x$lof_20 = lof_20

#as the previous stackoverflow post suggests: for each row, choose the highest LOF value 

x$max_lof = pmax(x$lof_10, x$lof_15, x$lof_20)

#view results:

 head(x)

         x          y    lof_10    lof_15    lof_20   max_lof
1 2.443382  4.2611753 0.9803894 0.9866732 0.9841705 0.9866732
2 2.397454 -0.3732838 1.0527592 1.4638348 1.6008284 1.6008284
3 2.617348  3.0435179 0.9952212 0.9945580 0.9715819 0.9952212
4 3.731156  4.1668976 1.0339001 1.0802826 1.0921033 1.0921033
5 1.103123  1.6642337 1.1260092 1.0773444 1.0650159 1.1260092
6 2.735938  4.3737450 0.9939896 0.9573139 0.9700123 0.9939896

因此,每一行的 LOF 分数是“max_lof”列的值。

有人可以告诉我是否正确解释了以前的stackoverflow帖子吗?我是否也正确编写了 R 代码?

谢谢

注意:我仍然不确定1)从我最初的问题中,

即较大的“k”值是否会导致识别的异常值更少(直方图左偏),但识别出的那些异常值更“极端”?


推荐阅读