有条件地选择 dplyr 中特定比例的值是 NA 的列

Conditionally selecting columns in dplyr where certain proportion of values is NA

数据

我正在处理类似于下面生成的 data.frame 的数据集:

set.seed(1)
dta <- data.frame(observation = 1:20,
                  valueA = runif(n = 20),
                  valueB = runif(n = 20),
                  valueC = runif(n = 20),
                  valueD = runif(n = 20))
dta[2:5,3] <- NA
dta[2:10,4] <- NA
dta[7:20,5] <- NA

这些列有 NA 个值,最后一列有超过 60% 的观测值 NAs

> sapply(dta, function(x) {table(is.na(x))})
$observation

FALSE 
   20 

$valueA

FALSE 
   20 

$valueB

FALSE  TRUE 
   16     4 

$valueC

FALSE  TRUE 
   11     9 

$valueD

FALSE  TRUE 
    6    14 

问题

我希望能够删除 dplyr 管道中的这一列,以某种方式将其传递给 select 参数。

尝试次数

这可以在 base 中轻松完成。例如,对于小于 50% NAs 的 select 列,我可以这样做:

dta[, colSums(is.na(dta)) < nrow(dta) / 2]

产生:

> head(dta[, colSums(is.na(dta)) < nrow(dta) / 2], 2)
  observation    valueA    valueB    valueC
1           1 0.2655087 0.9347052 0.8209463
2           2 0.3721239        NA        NA

任务

我有兴趣在 dplyr 管道中实现相同的灵活性:

Vectorize(require)(package = c("dplyr",         # Data manipulation
                               "magrittr"),     # Reverse pipe

char = TRUE)

dta %<>%
  # Some transformations I'm doing on the data
  mutate_each(funs(as.numeric)) %>% 
  # I want my select to take place here

也许像这样?

dta %>% select(which(colMeans(is.na(.)) < 0.5)) %>% head
#  observation    valueA    valueB    valueC
#1           1 0.2655087 0.9347052 0.8209463
#2           2 0.3721239        NA        NA
#3           3 0.5728534        NA        NA
#4           4 0.9082078        NA        NA
#5           5 0.2016819        NA        NA
#6           6 0.8983897 0.3861141        NA

已更新,使用 colMeans 而不是 colSums,这意味着您不再需要除以行数。

并且,仅作记录,在 base R 中,您还可以使用 colMeans:

dta[,colMeans(is.na(dta)) < 0.5]

我们可以在 summarise_each/unlist

得到逻辑向量后使用 magrittr 中的 extract
library(magrittr)
library(dplyr)
dta %>% 
    summarise_each(funs(sum(is.na(.)) < n()/2)) %>% 
    unlist() %>%
    extract(dta,.)

或使用 Filter 来自 base R

Filter(function(x) sum(is.na(x)) < length(x)/2, dta)

或者稍微紧凑的选项是

Filter(function(x) mean(is.na(x)) < 0.5, dta)

我认为这样做可以:

dta %>% select_if(~mean(is.na(.)) < 0.5) %>% head() 


 observation    valueA    valueB    valueC
  1           0.2655087 0.9347052 0.8209463
  2           0.3721239        NA        NA
  3           0.5728534        NA        NA
  4           0.9082078        NA        NA
  5           0.2016819        NA        NA
  6           0.8983897 0.3861141        NA

`

也许是 2020 年的更新,现在 dplyr 达到了 1.0.0,其中包含 where():

dta %>% select(where(function(x) sum(is.na(x)) / length(x) < 0.5))