我常用的缺失值插补方法

2023-02-14 17:07:55 浏览数 (1)

有的时候,面对一个有缺失值的数据,我只想赶紧把它插补好,此时的我并不在乎它到底是怎么缺失、插补质量如何等,我只想赶紧搞定缺失值,这样好继续进行接下来的工作。

今天这篇推文就是为这种情况准备的!

之前介绍过一个非常好用的缺失值插补R包:R语言缺失值插补之simputation包,支持管道符,使用起来非常简单且优雅,而且支持的方法的也非常多。但是它有一个最大的问题,不能一次性填补整个数据集的缺失值。

比如我有一个数据集,我知道它有缺失值,但是不知道在哪些列,但是我只想快速填补所有的缺失值,这时候这个R包就点力不从心了。

关于R语言中的缺失值插补,大家遇到最多的教程应该是mice包,不过我不太常用,所以就不介绍了。

一般来说,如果只是简单的均值或中位数填补的话,不需要R包,自己写一行简单的代码就搞定了。

均值/中位数/最大值/最小值等

新建一个有缺失值的数据集。

代码语言:javascript复制
set.seed(12)
df <- data.frame(a = sample(c(NA,NA,1:5),10, replace = T),
                 b = sample(c(NA,NA,1:10),10, replace = T),
                 c = sample(c(NA,1:5),10, replace = T),
                 d = sample(c(NA,1:3),10, replace = T)
                 )

df
##     a  b  c d
## 1  NA NA NA 1
## 2  NA NA  1 2
## 3   5  7  2 1
## 4   1  4  5 2
## 5   4 10  4 1
## 6   3  8  5 3
## 7   3  8  3 3
## 8   2  5  4 3
## 9  NA  6  4 3
## 10  1  2 NA 2

现在这个数据集有7个缺失值,我不想知道这些缺失值的具体情况,只想立马把它们填补好,不然没法进行下一步操作!

代码语言:javascript复制
table(is.na(df))
## 
## FALSE  TRUE 
##    33     7

均值插补:

代码语言:javascript复制
# 用每一列的均值插补
df1 <- sapply(df, function(x){
  x[is.na(x)] <- mean(x, na.rm=T)
  x
  })
df1
##              a     b   c d
##  [1,] 2.714286  6.25 3.5 1
##  [2,] 2.714286  6.25 1.0 2
##  [3,] 5.000000  7.00 2.0 1
##  [4,] 1.000000  4.00 5.0 2
##  [5,] 4.000000 10.00 4.0 1
##  [6,] 3.000000  8.00 5.0 3
##  [7,] 3.000000  8.00 3.0 3
##  [8,] 2.000000  5.00 4.0 3
##  [9,] 2.714286  6.00 4.0 3
## [10,] 1.000000  2.00 3.5 2
table(is.na(df1))
## 
## FALSE 
##    40

中位数插补:

代码语言:javascript复制
# 用每一列的中位数插补
df2 <- sapply(df, function(x){
  x[is.na(x)] <- median(x, na.rm=T)
  x
  })

table(is.na(df2))
## 
## FALSE 
##    40

像这种比较简单的插补方法,比如均数、中位数、最大值,最小值等方法,也可以通过Hmisc包实现。

代码语言:javascript复制
library(Hmisc)

impute(df$a, fun = mean) # median, max, min
##         1         2         3         4         5         6         7         8 
## 2.714286* 2.714286*  5.000000  1.000000  4.000000  3.000000  3.000000  2.000000 
##         9        10 
## 2.714286*  1.000000

不过也是一列一列的进行插补,如果同时有多列都有缺失值,也要配合其他函数完成。

代码语言:javascript复制
# 支持用常数插补!
impute(df$a, fun = 2)
##  1  2  3  4  5  6  7  8  9 10 
## 2* 2*  5  1  4  3  3  2 2*  1

其他常见R包

最常见的比如KNN插补、随机森林插补等。

KNN插补可以通过impute,这个包在bioconductor上,不在cran上;另外这个包不接受data.frame格式,需要变成matrix格式!

代码语言:javascript复制
library(impute)

impute.knn(as.matrix(df))$data
##              a    b        c d
##  [1,] 0.000000  0.0 0.000000 1
##  [2,] 2.111111  5.2 1.000000 2
##  [3,] 5.000000  7.0 2.000000 1
##  [4,] 1.000000  4.0 5.000000 2
##  [5,] 4.000000 10.0 4.000000 1
##  [6,] 3.000000  8.0 5.000000 3
##  [7,] 3.000000  8.0 3.000000 3
##  [8,] 2.000000  5.0 4.000000 3
##  [9,] 2.111111  6.0 4.000000 3
## [10,] 1.000000  2.0 3.444444 2

还可以通过VIM等包实现,支持data.frame格式:

代码语言:javascript复制
library(VIM)

kNN(df)[1:ncol(df)]
##    a  b c d
## 1  3  7 3 1
## 2  3  7 1 2
## 3  5  7 2 1
## 4  1  4 5 2
## 5  4 10 4 1
## 6  3  8 5 3
## 7  3  8 3 3
## 8  2  5 4 3
## 9  3  6 4 3
## 10 1  2 4 2

随机森林插补可以使用missForest,使用起来也是非常简单方便:

代码语言:javascript复制
library(missForest)

missForest(df)$ximp
##       a         b    c d
## 1  3.68  8.418333 3.39 1
## 2  2.86  5.790667 1.00 2
## 3  5.00  7.000000 2.00 1
## 4  1.00  4.000000 5.00 2
## 5  4.00 10.000000 4.00 1
## 6  3.00  8.000000 5.00 3
## 7  3.00  8.000000 3.00 3
## 8  2.00  5.000000 4.00 3
## 9  2.28  6.000000 4.00 3
## 10 1.00  2.000000 4.15 2

以上就是我常用的缺失值插补R包,除此之外,做机器学习的专用包caret/mlr3/tidymodels等,也包含很多缺失值处理的方法,还有tidyverse也有缺失值处理的函数,大家可以自行探索。

此外,缺失值插补在crantask view里面有一个专题:Missing Data,大家感兴趣的可以自己查看,里面有R语言所有和缺失值插补有关的R包介绍!

0 人点赞