R极简教程-8:缺失值与异常值
OK,数据都进来了,下一步要做的是查看数据质量怎么样?尤其要关注的两个问题是,有没有缺失值和异常值。
缺失值
这个很好理解,就是数据不见了呗。比如采集了5000行500列数据,其中某几个数据因为采集或者记录原因失败了,就造成了缺失值。缺失值在R语言中一般用NA来代替。(比如读取文件的时候,如果有些行有些列没有数值,就会自动填为NA)。
R使用NA(不可得)代表缺失值、NaN(不是一个数)代表不可能的值、Inf和-Inf代表正无穷和负无穷,函数is.na()、is.nan()、is.infinite()可分别用于识别缺失值、不可能值和无穷值。
再读取了一批文件以后,我们可以用is.na()
函数来查看缺失值:
> A <- matrix(1:36,6,6)
> A
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 7 13 19 25 31
[2,] 2 8 14 20 26 32
[3,] 3 9 15 21 27 33
[4,] 4 10 16 22 28 34
[5,] 5 11 17 23 29 35
[6,] 6 12 18 24 30 36
> A[1,3] <- NA
> A[5,2] <- NA
> A[6,6] <- NA
> A
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] 1 7 NA 19 25 31
[2,] 2 8 14 20 26 32
[3,] 3 9 15 21 27 33
[4,] 4 10 16 22 28 34
[5,] 5 NA 17 23 29 35
[6,] 6 12 18 24 30 NA
> is.na(A)
[,1] [,2] [,3] [,4] [,5] [,6]
[1,] FALSE FALSE TRUE FALSE FALSE FALSE
[2,] FALSE FALSE FALSE FALSE FALSE FALSE
[3,] FALSE FALSE FALSE FALSE FALSE FALSE
[4,] FALSE FALSE FALSE FALSE FALSE FALSE
[5,] FALSE TRUE FALSE FALSE FALSE FALSE
[6,] FALSE FALSE FALSE FALSE FALSE TRUE
>
再上面的程序中,我们首先建立了一个矩阵A,然后在其中人工加入了三个缺失值。然后用is.na()可以判断,每一个数据是不是缺失值。
在这里which函数是非常有用的。(其实which函数再R语言中是一个非常重要的函数。)可以用which看出是那几个位置有错误。
> which(is.na(A))
[1] 11 13 36
我们定义的是一个矩阵,但是which出来的结果确实数字,这是因为which会将Matrix或者DataFrame展开成Vector,然后计算缺失值。就类似于as.vector(A)
一样。如果想要确切地知道哪行哪列的数据是缺失值,可以在which函数中加入参数:
> which(is.na(A),arr.ind = T)
row col
[1,] 5 2
[2,] 1 3
[3,] 6 6
>
可以看出,我们准确地找到了NA值所在的行列。
缺失值可视化
简而言之就是,想要直观地看看你的数据中缺失值在哪里。最直观的看法就是使用热图(heatmap)(不是地图上红一块绿一块那种热力图!),我之前的教程里用到的pheatmap函数就是这方面的好工具。
在之前,我们用命令is.na()
的时候,其实已经看出缺失值的,但是目前我们数据缺省值很少,如果缺失值多怎么办?
> #随机产生一个40行50列的矩阵
> A <- matrix(rnorm(2000,0,1),40,50)
> #再其中增加200个NA值。
> A[sample(1:2000,200)] <- NA
> #生成一个全部是1,维度与A相同的矩阵
> B <- matrix(1,nrow=nrow(A),ncol=ncol(A))
> #将B中A的NA值,设置为0
> B[is.na(A)] <- 0
> #用pheatmap函数plot所有的缺失值
> pheatmap(B,cluster_rows = F,cluster_cols = F)
上图中的蓝点就是这批数据中的缺失值。
下面的话出自某网络博客。我觉得说的很对:
识别缺失数据的数目、分布和模式有两个目的:分析生成缺失数据的潜在机制,评价缺失数据对回答实质性问题的影响。我们需要弄清楚以下几个问题:
缺失数据的比例多大?
缺失数据是否集中在少数几个变量上,或者广泛存在?
缺失是随机产生的吗?
缺失数据间的相关性或与可观测数据间的相关性,是否可以表明产生缺失值的机制?
这些就是不那么容易解决的问题了,比如说,每一列缺失数据比例多大,可以很简单的用一行代码算出来:
> colMeans(is.na(A))
至于第二个问题,缺失值是否存在某一些变量上,这样可以通过做barplot来看。
> barplot(colMeans(is.na(A)),names.arg = 1:50)
可以看出,大部分的列都有缺失值(那当然,我们是随机生成的缺失值。)
缺失值处理
一般来说,处理缺失值有两种办法:直接把数据删掉,或者用补足。删掉的意思就是,如果某一行出现了缺失值,无论这一行中缺失值有多少,直接将其删除,整行删除。补充的意思是,用附近的一些数据的值,计算一个估计值去填补这一个数。
直接删除的优缺点分别是:方便快捷,代码好写,但是有可能太多信息都会被遗漏掉。
补足(impute):的优缺点分别是:尽量保持了信息,但是如果一行数据10个,有9个是NA,那么补出来的数据也不会有多高的质量。
所以,缺失值处理是一个比较需要经验的问题,一般来说我的处理办法是,如果样本数据很多,比如列有上百个,那就先对列做缺失值比例计算,将缺失达到10%的样本直接删除。然后再剩下的数据中,按行做缺失值比例调查,将比例高达一定阈值(比如20%)的删掉,然后对剩下的数据做impute过程。
impute过程可以使用impute
R包,其中的impute.knn()
函数很好用:
完整代码如下:
> A <- matrix(rnorm(2000,0,1),40,50)
> A[sample(1:2000,200)] <- NA
> colRatio <- colMeans(is.na(A))
> col_remain <- which(colRatio < 0.2)
> length(col_remain)
[1] 49
> C <- A[,col_remain]
>
> rowRatio <- rowMeans(is.na(C))
> row_remain <- which(rowRatio < 0.2)
> length(row_remain)
[1] 40
>
> D <- C[row_remain,]
> dim(D)
[1] 40 49
> library(impute)
> Final <- impute.knn(D)
异常值
异常值顾名思义就是偏离了“寻常值”的数据。但是多“异常”的值才能成为异常值,这就得看研究项目而定了。有些时候,异常值才是一个科研项目中应该去研究的问题。一般来说,有一个想对通用的检测异常值的标准,就是均值±三倍标准差。这个很好理解,你的均值是数据大部分“寻常值”的所在位置,标准差是其差异程度。那么3倍标准差很明显就说明一个数据严重地偏离了均值了。
下面我们用代码来写一下:
A <- rnorm(1000,0,1)
outlier_above <- which(A > mean(A) + 3*sd(A))
outlier_below <- which(A < mean(A) - 3*sd(A))
我们可以看到,在我随机生成了1000个随机数以后(正态分布,均值为0,标准差为1),只有4个数达到了异常值上届,也只有3个数达到了异常值的下届。
下面我们尝试把这些异常值画出来,其实代码特别简单:
boxplot(A)
该图的中文名叫做箱线图,其中上下的小圆点就是异常值的意思。最上边的横线是上边界,最下边是下边界,中间的箱体,上边缘是上四分位数,下边缘是下四分位数,中间最粗的横线是中位数(不是均值)。
个人觉得,有一个特别简单找出异常值的办法,就是使用boxplot函数:
> tmp <- boxplot(A)
> tmp$out
[1] 3.338426 2.854525 3.500505 3.071915 2.853435 3.009685 -3.315813 -3.564299
[9] -3.374682 2.744321
>
直接将boxplot存储成为一个变量tmp,tmp中的out子元素就是异常值。
对于异常值的处理也是花样繁多,有的人就直接把所有异常值设置成为NA,然后就回到了上边的缺失值部分。也有人保留他们,总之这方面算法很多,不一而足。异常值产生的原因很多,有很多时候,异常值其实不仅有意义,而且很重要:
比如,探查人流涌动,就是通过异常值来看那些地方突然人数激增?探查宇宙射线一类的科研,最关注的东西永远都是异常值。再大数定律横行其道的今天,几乎各种数据都是需要量化来达到规律总结的,这恰好令了“天鹅”一类事件越来越难以估计。所以对于异常值的处理和解读,其实正是数据分析人员的水平高下所在。
在这一部分,我简单介绍了一下R语言中的缺失值和异常值。
上一篇: 【剑指offer刷题】--顺时针打印矩阵
推荐阅读