有条件地选择dplyr中的列,其中某些比例的值为NA

Kon*_*rad 9 r filter dataframe na dplyr

数据

我正在使用类似于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
Run Code Online (Sandbox Code Playgroud)

列具有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 
Run Code Online (Sandbox Code Playgroud)

问题

我希望能够以dplyr某种方式将管道中的这一列移到select参数中.

尝试

这可以很容易地完成base.例如,要选择低于50%的 列,NAs我可以这样做:

dta[, colSums(is.na(dta)) < nrow(dta) / 2]
Run Code Online (Sandbox Code Playgroud)

产生:

> 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
Run Code Online (Sandbox Code Playgroud)

任务

我有兴趣在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
Run Code Online (Sandbox Code Playgroud)

tal*_*lat 15

也许这样吗?

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
Run Code Online (Sandbox Code Playgroud)

更新colMeans替代的colSums,这意味着你不需要通过行数更多的分.

而且,只是为了记录,在基地R你也可以使用colMeans:

dta[,colMeans(is.na(dta)) < 0.5]
Run Code Online (Sandbox Code Playgroud)


akr*_*run 5

我们可以在获得逻辑向量后使用extractfrommagrittrsummarise_each/unlist

library(magrittr)
library(dplyr)
dta %>% 
    summarise_each(funs(sum(is.na(.)) < n()/2)) %>% 
    unlist() %>%
    extract(dta,.)
Run Code Online (Sandbox Code Playgroud)

或者使用Filter来自base R

Filter(function(x) sum(is.na(x)) < length(x)/2, dta)
Run Code Online (Sandbox Code Playgroud)

或者一个稍微紧凑的选项是

Filter(function(x) mean(is.na(x)) < 0.5, dta)
Run Code Online (Sandbox Code Playgroud)

  • @DavidArenburg 我还可以补充一点,这种转换是更长的工作流程的一部分,包括最后的图表。实际上,这些转换仅用于生成特定更改的目的,因此除了性能问题之外,代码在行上多读少读是有意义的 * 获取主数据 -&gt; 在其上做一些事情 -&gt;生成图表*。这只是一本很好的书,我希望读得更简洁。正如“通过教学,我们学习”正确指出的那样,主要是关于工作流程而不是其他任何事情。为我辩护,我想说这在某种程度上是品味问题。 (3认同)
  • 我有点无法理解整个问题。似乎“dta[colMeans(is.na(dta)) &lt; .5]”是最简洁/矢量化/简单/可读的解决方案,所有这些“Filter”或“dplyr/magrittr”内容有什么意义?我大概已经老了。 (2认同)
  • @DavidArenburg您确实了解“colMeans”、“colSums”等将数据转换为“矩阵”。它可能没有那么有效(可能是我错了)。但是,我喜欢“Filter”,因为它增添了一种异国情调(就像这个[问题]中的“filter”一样(http://stackoverflow.com/questions/34851754/in-r-how-to-sum-by -flowing-row-in-a-data-frame/34852022#34852022)。除此之外,“magrittr”的东西仅适用于“dplyr/magritr”粉丝(我的拙见)。 (2认同)
  • 实际上 `is.na` 已经转换为矩阵了。`col*` 函数已经在矩阵上运行。因此,如果您愿意,我们可以执行“dta[colMeans(sapply(dta, is.na)) &lt; .5]”,并且它仍然非常可读。在该链接的问题中,“过滤器”实际上是最简洁/矢量化/简单的解决方案,而不是一种奇特的解决方案。但我猜你不是我农庄的地址。 (2认同)

小智 5

我认为这可以做到:

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
Run Code Online (Sandbox Code Playgroud)

`


MS *_*nds 5

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

dta %>% select(where(function(x) sum(is.na(x)) / length(x) < 0.5))
Run Code Online (Sandbox Code Playgroud)