如何从数据集中去除异常值

114

我有一些关于美丽指数与年龄的多元数据。年龄从20到40,每两岁为一个间隔(20、22、24...40),对于每条数据记录,它们都有一个年龄和一个1-5的美丽评分。当我用箱线图绘制这些数据时(X轴表示年龄,Y轴表示美丽评分),会有一些离群值在每个箱型图须之外。

我想要从数据框中移除这些离群值,但我不确定R是如何计算箱线图的离群值的。下面是我的数据可能看起来像的示例。 enter image description here


3
boxplot函数返回离群值(以及其他统计信息),但是这些信息是不可见的。尝试使用foo <- boxplot(...); foo命令,并阅读?boxplot来理解输出结果。 - Joshua Ulrich
30
相关链接:http://davidmlane.com/ben/outlier.gif - eyjo
1
你能发一下数据的链接吗? - wordsforthewise
12个回答

165

5
非常优雅。谢谢。但是如果分布有多个模式并且异常值确实只有少数且分散的话,需要小心。 - KarthikS
1
如果您能够在数据集中获取它们的索引,那就太好了。您现在所做的是基于数据值进行过滤。如果箱线图也进行分组,那么并不一定每个组中都会有相同的数据值成为异常值。 - adam
3
还有一点需要提到的是,这并不会改变数据集。这只是一种过滤方法。因此,如果您打算在没有异常值的情况下使用数据集,请将其分配给一个变量。例如:result = x[!x %in% boxplot.stats(x)$out] - Victor Augusto
@Adam,请查看下面我回答的内容以解决这个问题。 - Niels Janssen
关于boxplot.stats()的问题:有人知道这个函数如何定义异常值吗?例如,基于平均值的某个标准差倍数? - bt3
@bt3 如果有任何值落在箱线图的须之外,那么这些值就被认为是异常值。默认情况下,箱线图的须长度是箱体尺寸的1.5倍;而箱体的定义基于第一四分位数和第三四分位数。详见 https://www.rdocumentation.org/packages/grDevices/versions/3.6.2/topics/boxplot.stats - J. Win.

136

好的,你应该对数据集应用类似这样的东西。不要替换并保存,否则会破坏你的数据! 顺便说一句,你几乎永远不应该从你的数据中移除异常值:

remove_outliers <- function(x, na.rm = TRUE, ...) {
  qnt <- quantile(x, probs=c(.25, .75), na.rm = na.rm, ...)
  H <- 1.5 * IQR(x, na.rm = na.rm)
  y <- x
  y[x < (qnt[1] - H)] <- NA
  y[x > (qnt[2] + H)] <- NA
  y
}

看它如何运作:

set.seed(1)
x <- rnorm(100)
x <- c(-10, x, 10)
y <- remove_outliers(x)
## png()
par(mfrow = c(1, 2))
boxplot(x)
boxplot(y)
## dev.off()

再次强调,您不应该自己处理离群值,因为离群值就是存在的!=)

编辑:我已将na.rm = TRUE设置为默认值。

编辑2:删除了quantile函数,添加了下标,从而使函数更快!=)

输入图像描述


3
好的,我这里有一些疑问。您想从数据中去除异常值,以便使用boxplot绘制它们。这是可以处理的,而且您应该标记@Prasad的答案,因为他回答了您的问题。如果您想使用“异常值规则” q +/- (1.5 * H)来排除异常值,然后进行分析,请使用此功能。顺便说一下,我是自己编写的,没有通过谷歌搜索,所以有可能我已经重新发明了轮子...... - aL3xa
为了更加通用,将1.5作为函数的参数,并设置默认值。同时允许单独设置下限和上限范围:c(-1.5,+1.5) - smci
8
"异常值注定就是这样吗?" 不一定。它们可能来自测量误差,必须认真审查。当异常值太大时,它可能意味着某些事情,也可能不意味着太多。这就是为什么(至少在生物学中)中位数通常比均值更能反映一个总体的原因。 - Rodrigo
不错。用标准差(SD)代替四分位距(IQR)怎么样?例如,H <- 8 * sd(x, na.rm = na.rm)?这样会排除均值上下8个标准差之外的值吗? - Aby
确实会这样。 - moodymudskipper

31

在制作箱线图时,使用outline = FALSE选项(请查看帮助文档!)。

> m <- c(rnorm(10),5,10)
> bp <- boxplot(m, outline = FALSE)

这里输入图片描述


4
确实,这将从箱线图中去除异常值,但我想从数据框中删除异常值。 - Dan Q
2
我明白了,那么正如@Joshua所说,您需要查看boxplot函数返回的数据(特别是列表中的“out”和“group”项)。 - Prasad Chalasani

17

boxplot函数返回用于绘图的值(实际上是由bxp()函数完成绘图):

bstats <- boxplot(count ~ spray, data = InsectSprays, col = "lightgray") 
#need to "waste" this plot
bstats$out <- NULL
bstats$group <- NULL
bxp(bstats)  # this will plot without any outlier points

我故意没有回答具体的问题,因为我认为删除"离群值"是统计学上的不良行为。我认为在箱线图中不绘制它们是可以接受的做法,但是仅仅因为它们超过某个标准差或四分位数范围而将其删除是对观察记录的系统性和非科学性的篡改。


5
不知道提问的原因而回避问题也不是一个好习惯。是的,从数据中移除'离群值'并不好,但有时你需要没有离群值的数据来完成特定任务。最近我在一个统计作业中,我们需要将一个数据集去除其离群值以确定最佳的回归模型来使用。就是这样! - Alex Essilfie
4
我不认为你在这方面得到的“确定最佳回归模型”的建议特别有说服力。相反,如果你需要删除异常值以达到模糊陈述的目的,那么我认为这反映了建议者的能力不足,而不是证明我的立场无效。 - IRTFM
我想当你知道你正在消除“噪音”,尤其是在生理数据方面时,这是合法的。 - roscoe1895
是的。如果您有充分的理由相信一个独立的进程创建了该信号,那么这就是从数据中删除它的正当理由。 - IRTFM

13

我查找了与去除离群值相关的软件包,并找到了这个软件包(令人惊讶地称为"outliers"!):https://cran.r-project.org/web/packages/outliers/outliers.pdf
如果你看一下里面,你会看到不同的去除离群值的方式,其中我发现rm.outlier最方便使用,正如它在上面链接中所说的: “如果利用统计检验检测到并确认了异常值,则此函数可以将其删除或替换为样本均值或中位数”,以下是相同来源的使用部分:
"Usage

rm.outlier(x, fill = FALSE, median = FALSE, opposite = FALSE)

参数
x:一个数据集,通常是向量。如果参数是数据框,则通过sapply从每个列中删除异常值。当给定矩阵时,apply会应用相同的行为。
fill:如果设置为TRUE,则在异常值位置放置中位数或均值。否则,异常值将被简单地移除。
median:如果设置为TRUE,则在异常值替换中使用中位数而不是平均数。如果设置为TRUE,则相反值(如果最大值与平均值的差异最大,则提供最小值,反之亦然)。


这听起来很不错,但如果您的数据框中有时间序列列,它会改变时间序列。 - PM0087

9
x<-quantile(retentiondata$sum_dec_incr,c(0.01,0.99))
data_clean <- data[data$attribute >=x[1] & data$attribute<=x[2],]

我觉得去除异常值非常容易。在上面的例子中,我只是提取属性值的2个百分点到98个百分点。


8

不会:

z <- df[df$x > quantile(df$x, .25) - 1.5*IQR(df$x) & 
        df$x < quantile(df$x, .75) + 1.5*IQR(df$x), ] #rows

您可以轻松地完成这项任务吗?

6

在 @sefarkas 的建议基础上,采用分位数作为截断点,可以尝试以下选项:

newdata <- subset(mydata,!(mydata$var > quantile(mydata$var, probs=c(.01, .99))[2] | mydata$var < quantile(mydata$var, probs=c(.01, .99))[1]) ) 

这将删除超过99分位数的点。要注意像aL3Xa所说的保留异常值。仅在获取数据的替代保守视图时才应删除。


0.91 还是 0.99?就像这样 mydata$var < quantile(mydata$var, probs=c(.01, .91))[1]) 或者 mydata$var < quantile(mydata$var, probs=c(.01, .99))[1]) - Komal Rathi
如果您有特定的原因使用91百分位数而不是99百分位数,您可以使用它。这只是一个启发式方法。 - KarthikS

3

实现该目标的一种方式是

my.NEW.data.frame <- my.data.frame[-boxplot.stats(my.data.frame$my.column)$out, ]

或者

my.high.value <- which(my.data.frame$age > 200 | my.data.frame$age < 0) 
my.NEW.data.frame <- my.data.frame[-my.high.value, ]

1
离群值与峰值非常相似,因此峰值检测器可用于识别离群值。 这里 描述的方法使用 z-score 具有相当好的性能。页面中部的动画演示了该方法对离群值或峰值的信号。

峰值并不总是等同于离群值,但它们经常相似。

下面显示了一个示例:该数据集通过串行通信从传感器读取。偶尔发生串行通信错误、传感器错误或两者同时出现会导致重复、明显错误的数据点。这些点没有统计价值。可以认为它们不是离群值,而是错误。z-score 峰值检测器能够对虚假数据点进行信号处理,并生成干净的结果数据集:enter image description here


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接