R语言-异常数据处理1

R语言:异常数据处理

前言

  在数据处理中,尤其在作函数拟合时,异常点的出现不仅会很大程度的改变函数拟合的效果,而且有时还会使得函数的梯度出现奇异梯度,这就导致算法的终止,从而影响研究变量之间的函数关系。为了有效的避免这些异常点造成的损失,我们需要采取一定的方法对其进行处理,而处理的第一步便是找到异常点在数据中的位置。

  什么是异常值?如何检测异常值?

目录

 1. 单变量异常值检测

 2. 使用LOF(local outlier factor,局部异常因子)进行异常检测

 3. 通过聚类的方法检验异常值

 4. 检验时间序列数据里面的异常值

 5. 讨论

主要程序包

    install.packages(c("DMwR","dprep"))
    library(DMwR)
    library(dprep)

1. 单变量异常值检测

  这节主要讲单变量异常值检测,并演示如何将它应用到多元(多个自变量)数据中。使用函数boxplot.stats()实现单变量检测,该函数根据返回的统计数据生成箱线图。在上述函数的返回结果中,有一个参数out,它是由异常值组成的列表。更明确的说就是里面列出了箱线图中箱须线外面的数据点。其中参数coef可以控制箱须线从箱线盒上延伸出来的长度,关于该函数的更多细节可以通过输入‘?boxplot.ststs’查看。

  画箱线图:

    set.seed(3147)
    #产生100个服从正态分布的数据
    x <- rnorm(100)
    summary(x)
    #输出异常值
    boxplot.stats(x)$out
    #绘制箱线图
    boxplot(x)

%E5%BC%82%E5%B8%B8%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86-Rplot01.bmp

  如上的单变量异常检测可以用来发现多元数据中的异常值,通过简单搭配的方式。在下例中,我们首先产生一个数据框df,它有两列x和y。之后,异常值分别从x和y检测出来。然后,我们获取两列都是异常值的数据作为异常数据。

    x <- rnorm(100)
    y <- rnorm(100)
    # 生成一个包含列名分别为x与y的数据框df
    df <- data.frame(x, y)
    rm(x,y)
    head(df)
    # 连接数据框df
    attach(df)
    # 输出x中的异常值
    (a <- which(x %in% boxplot.stats(x)$out))
    # 输出y中的异常值
    (b <- which(y %in% boxplot.stats(y)$out))
    # 断开与数据框的连接
    detach(df)
    # 输出x,y相同的异常值
    (outlier.list1 <- intersect(a,b))
    plot(df)
    # 标注异常点
    points(df[outlier.list1,], col="red", pch="+", cex=2.5)
    # x或y中的异常值
    (outlier.list2 <- union(a, b))
    plot(df)
    points(df[outlier.list2,], col="blue", pch="x", cex=2)

%E5%BC%82%E5%B8%B8%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86-Rplot02.bmp

  当有三个以上的变量时,最终的异常值需要考虑单变量异常检测结果的多数表决。当选择最佳方式在真实应用中进行搭配时,需要涉及领域知识。

2. 使用LOF(local outlier factor,局部异常因子)进行异常检测

  LOF(局部异常因子)是一种基于密度识别异常值的算法。算法实现是:将一个点的局部密度与分布在它周围的点的密度相比较,如果前者明显的比后者小,那么这个点相对于周围的点来说就处于一个相对比较稀疏的区域,这就表明该点事一个异常值。(使用LOF,一个点的局部密度会与它的邻居进行比较。如果前者明显低于后者(有一个大于1 的LOF值),该点位于一个稀疏区域,对于它的邻居而言,这就表明,该点是一个异常值。)LOF算法的缺点是它只对数值型数据有效。

  lofactor()函数使用LOF算法计算局部异常因子,并且它在DMwR和dprep包中是可用的。下面将介绍一个使用LOF进行异常检测的例子,k是用于计算局部异常因子的邻居数量。下图呈现了一个异常值得分的密度图。

    > library(DMwR)
    > # 移除“Species”这个鸢尾花类别列数据
    > iris2 <- iris[,1:4]
    > # k是计算局部异常因子所需要判断异常点周围的点的个数
    > outlier.scores <- lofactor(iris2, k=5)
    > # 绘制异常值得分的密度分布图
    > plot(density(outlier.scores))
    > # 挑出得分排前五的数据作为异常值
    > outliers <- order(outlier.scores, decreasing = T)[1:5]
    > # 输出异常值
    > print(outliers)
    [1]  42 107  23 110  63

%E5%BC%82%E5%B8%B8%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86-Rplot03.bmp

  接下来对鸢尾花数据进行主成分分析,并利用产生的前两个主成分绘制成双标图来显示异常值。

    > n <- nrow(iris2)
    > labels <- 1:n
    > # 除了异常值以外所有的数据用"."标注
    > labels[-outliers] <- "."
    > biplot(prcomp(iris2), cex=.8, xlabs=labels)

%E5%BC%82%E5%B8%B8%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86-Rplot04.bmp

  上面的代码中,prcomp()实现对数据集iris2的主成分分析,biplot()取主成分分析结果的前两列数据也就是前两个主成分绘制双标图。上图中,x轴和y轴分别代表第一、二主成分,箭头指向了原始变量名,其中5个异常值分别用对应的行号标注。

  我们也可以通过pairs()函数绘制散点图矩阵来显示异常值,其中异常值用红色的'+'标注:

    > # 使用rep()生成n个"."
    > pch <- rep(".", n)
    > pch[outliers] <- "+"
    > col <- rep("black", n)
    > col[outliers] <- "red"
    > pairs(iris2, pch=pch, col=col)

%E5%BC%82%E5%B8%B8%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86-Rplot05.bmp

  Rlof包,对LOF算法的并行实现。它的用法与lofactor()相似,但是lof()有两个附加的特性,即支持k的多元值和距离度量的几种选择。如下是lof()的一个例子。在计算异常值得分后,异常值可以通过选择前几个检测出来。注意,目前包Rlof的版本在MacOS X和Linux环境下工作,但并不在windows环境下工作,因为它要依赖multicore包用于并行计算。

    library(Rlof)
    outlier.scores <- lof(iris2, k=5)
    
    # 尝试使用不同的k值
    # try with different number of neighbors (k=5,6,7,8,9 and 10)
    outlier.scores <- lof(iris2, k=c(5:10))

3. 通过聚类的方法检验异常值

  另外一种异常检测的方法是聚类。通过把数据聚成类,将那些不属于任务一类的数据作为异常值。比如,使用基于密度的聚类DBSCAN,如果对象在稠密区域紧密相连,它们将被分组到一类。因此,那些不会被分到任何一类的对象就是异常值。

  我们也可以使用k-means算法来检测异常。使用k-means算法,数据被分成k组,通过把它们分配到最近的聚类中心。然后,我们能够计算每个对象到聚类中心的距离(或相似性),并且选择最大的距离作为异常值。

    #####################################
    iris2 <- iris[,1:4]
    kmeans.result <- kmeans(iris2, centers=3)
    #class(kmeans.result)
    # 输出簇中心
    kmeans.result$centers
    #length(kmeans.result$centers)
    # 分类结果
    kmeans.result$cluster
    #mode(kmeans.result$cluster)
    # 计算数据对象与簇中心的距离
    centers <- kmeans.result$centers[kmeans.result$cluster, ]
    #class(centers)
    distances <- sqrt(rowSums((iris2 - centers)^2))
    # 挑选出前5个最大距离
    outliers <- order(distances, decreasing = T)[1:5]
    # 输出异常值
    print(outliers)
    print(iris2[outliers,])
    # 画出聚类结果
    plot(iris2[,c("Sepal.Length", "Sepal.Width")], pch="o", col=kmeans.result$cluster, cex=0.3)
    # 绘制类(簇)中心,用"*"标记
    points(kmeans.result$centers[,c("Sepal.Length", "Sepal.Width")], col=1:3, pch=8, cex=1.5)
    # 画出异常值,用"+"标记
    points(iris2[outliers,c("Sepal.Length", "Sepal.Width")], pch="+", col=4, cex=1.5)
    #####################################


    #####################################
    > iris2 <- iris[,1:4]
    > kmeans.result <- kmeans(iris2, centers=3)
    > #class(kmeans.result)
    > # 输出簇中心
    > kmeans.result$centers
      Sepal.Length Sepal.Width Petal.Length Petal.Width
    1     6.314583    2.895833     4.973958   1.7031250
    2     5.175758    3.624242     1.472727   0.2727273
    3     4.738095    2.904762     1.790476   0.3523810
    > #length(kmeans.result$centers)
    > # 分类结果
    > kmeans.result$cluster
      [1] 2 3 3 3 2 2 2 2 3 3 2 2 3 3 2 2 2 2 2 2 2 2 2 2 3 3 2 2 2 3 3 2
     [33] 2 2 3 2 2 2 3 2 2 3 3 2 2 3 2 3 2 2 1 1 1 1 1 1 1 3 1 1 3 1 1 1
     [65] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 3 1 1
     [97] 1 1 3 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    [129] 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
    > #mode(kmeans.result$cluster)
    > # 计算数据对象与簇中心的距离
    > centers <- kmeans.result$centers[kmeans.result$cluster, ]
    > #class(centers)
    > distances <- sqrt(rowSums((iris2 - centers)^2))
    > # 挑选出前5个最大距离
    > outliers <- order(distances, decreasing = T)[1:5]
    > # 输出异常值
    > print(outliers)
    [1] 119 118 132 123 106
    > print(iris2[outliers,])
        Sepal.Length Sepal.Width Petal.Length Petal.Width
    119          7.7         2.6          6.9         2.3
    118          7.7         3.8          6.7         2.2
    132          7.9         3.8          6.4         2.0
    123          7.7         2.8          6.7         2.0
    106          7.6         3.0          6.6         2.1
    > # 画出聚类结果
    > plot(iris2[,c("Sepal.Length", "Sepal.Width")], pch="o", col=kmeans.result$cluster, cex=0.3)
    > # 绘制类(簇)中心,用"*"标记
    > points(kmeans.result$centers[,c("Sepal.Length", "Sepal.Width")], col=1:3, pch=8, cex=1.5)
    > # 画出异常值,用"+"标记
    > points(iris2[outliers,c("Sepal.Length", "Sepal.Width")], pch="+", col=4, cex=1.5)
    #####################################

%E5%BC%82%E5%B8%B8%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86-Rplot06.bmp

  在上图中,聚类中心被标记为星号,异常值标记为'+'

4. 检验时间序列数据里面的异常值

  本部分讲述一个对时间序列数据进行异常检测的例子。在本例中,时间序列数据首次使用stl()进行稳健回归分解,然后识别异常值。

    ########################################
    # 使用稳健回归拟合
    f <- stl(AirPassengers, "periodic", robust=TRUE)
    (outliers <- which(f$weights < 1e-8))
    # 绘图布局
    op <- par(mar=c(0, 4, 0, 3), oma=c(5, 0, 4, 0), mfcol=c(4, 1))
    plot(f, set.pars=NULL)
    sts <- f$time.series
    # 画出异常值,用红色"x"标记
    points(time(sts)[outliers], 0.8*sts[,"remainder"][outliers], pch="x", col="red")
    par(op)
    ########################################
    > # 使用稳健回归拟合
    > f <- stl(AirPassengers, "periodic", robust=TRUE)
    > (outliers <- which(f$weights < 1e-8))
     [1]  79  91  92 102 103 104 114 115 116 126 127 128 138 139 140
    > # 绘图布局
    > op <- par(mar=c(0, 4, 0, 3), oma=c(5, 0, 4, 0), mfcol=c(4, 1))
    > plot(f, set.pars=NULL)
    > sts <- f$time.series
    > # 画出异常值,用红色"x"标记
    > points(time(sts)[outliers], 0.8*sts[,"remainder"][outliers], pch="x", col="red")
    > par(op)
    ########################################

%E6%97%B6%E9%97%B4%E5%BA%8F%E5%88%97%E5%BC%82%E5%B8%B8%E6%95%B0%E6%8D%AE%E5%A4%84%E7%90%86Rplot07.bmp

  在上图中,异常值用红色标记为'x'

5. 讨论

  LOF算法擅长检测局部异常值,但是它只对数值数据有效。Rlof包依赖multicore包,在Windows环境下失效。对于分类数据的一个快速稳定的异常检测的策略是AVF(Attribute Value Frequency)算法。

  一些用于异常检测的R包包括:

  extremevalues包:单变量异常检测

  mvoutlier包:基于稳定方法的多元变量异常检测

  outliers包:对异常值进行测验

参考资料

转载于:https://www.cnblogs.com/cloudtj/articles/5519964.html

  • 1
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
R语言中,可以使用一些方法来处理异常值。以下是几种常用的处理方法: 1. 删除异常值:可以直接删除包含异常值的数据点。可以使用`subset()`函数根据某些条件来筛选数据,或者使用`na.omit()`函数删除包含缺失值的数据点。 ```R # 删除异常值 data <- subset(data, condition) data <- na.omit(data) ``` 2. 替换异常值:可以使用其他数值来替代异常值。通常可以选择使用平均值、中位数或者众数来替代异常值。 ```R # 使用平均值替代异常值 mean_value <- mean(data, na.rm = TRUE) data[data > threshold] <- mean_value # 使用中位数替代异常值 median_value <- median(data, na.rm = TRUE) data[data > threshold] <- median_value # 使用众数替代异常值 mode_value <- Mode(data) data[data > threshold] <- mode_value ``` 3. 转换异常值:有时候可以通过对数据进行转换来减小异常值的影响。常用的转换方法包括对数转换、平方根转换等。 ```R # 对数转换 data_transformed <- log(data) # 平方根转换 data_transformed <- sqrt(data) ``` 4. 分组处理:可以根据某些特征或条件将数据分组,然后对每个分组内的异常值进行独立处理。 ```R # 将数据按照某个特征分组 grouped_data <- split(data, factor) # 对每个分组内的异常值进行处理 for (i in 1:length(grouped_data)) { grouped_data[[i]][grouped_data[[i]] > threshold] <- replacement_value } # 合并处理后的数据 data_processed <- do.call("rbind", grouped_data) ``` 需要注意的是,处理异常值时应该谨慎,并且应该在保持数据的完整性和可解释性的前提下进行处理。同时,处理异常值的方法应该基于对数据和问题的深入理解,并进行适当的敏感性分析和验证。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值