r - 异常值检测的邻域计算
问题描述
我正在使用 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)中描述的逻辑:
“k”值越大,识别出的异常值越少,但识别出的异常值更“极端”
较小的“k”值会导致识别出更多异常值,但识别出的异常值不那么“极端”
这个对吗?
解决方案
关于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”值是否会导致识别的异常值更少(直方图左偏),但识别出的那些异常值更“极端”?
推荐阅读
- dpdk - DPDK的轮询是怎么做的?
- javascript - 限制 object.map 中的结果数量
- c# - 如何使您的 CrystalDecision 为您的报表工作?
- c# - 应该避免在无参数构造函数中进行初始化吗?
- java - org.omg.CORBA.TRANSIENT:初始和转发的 IOR 不可访问 vmcid:IBM 次要代码:来自 IIB 应用程序的 E07
- java - Java 中的 compareTo() 函数
- node.js - 使用 dockerfile 安装新的 react 应用程序
- android - 将android连接到django时出现400错误请求
- ios - 如何获取配对的两个或多个蓝牙键盘(键盘)的属性或名称
- sql - 查找与 SQL 相关的路径(如 Dijkstra)