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

Posted

技术标签:

【中文标题】有条件地选择 dplyr 中某些值比例为 NA 的列【英文标题】:Conditionally selecting columns in dplyr where certain proportion of values is NA 【发布时间】:2016-04-23 11:16:19 【问题描述】:

数据

我正在使用类似于下面生成的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 的列我可以这样做:

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

【问题讨论】:

您可以使用Filter,即Filter(function(x) sum(is.na(x)) &lt; length(x)/2, dta) @akrun 一如既往,感谢您的帮助。我只是想知道,filter 不应该放弃观察吗?我有兴趣删除列而不是行。 它正在删除列,即Filter,大写为F @akrun 现在,我找到你了?Filter != ?filter :) 我用summarise_each 添加了一个解决方案。也许对你有帮助。 【参考方案1】:

也许像这样?

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,这意味着您不再需要除以行数。

而且,为了记录,在基础 R 中,您还可以使用 colMeans

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

【讨论】:

【参考方案2】:

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

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

【讨论】:

【参考方案3】:

我认为这样做可以:

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

`

【讨论】:

【参考方案4】:

在得到summarise_each/unlist的逻辑向量后,我们可以使用magrittr中的extract

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

或者从base R使用Filter

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

或者稍微紧凑的选项是

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

【讨论】:

我有点无法理解整个问题。似乎dta[colMeans(is.na(dta)) &lt; .5] 是最简洁/矢量化/简单/可读的解决方案,所有这些Filterdplyr/magrittr 的东西有什么意义?我可能老了。 @DavidArenburg 你知道colMeanscolSums 等将数据转换为matrix。它可能没有那么有效(可能是我错了)。但是,我喜欢Filter,因为它为它增添了一种异国情调(就像question 中的filter。除此之外,magrittr 的东西仅供dplyr/magritr 的粉丝使用(我的拙见)。跨度> 其实is.na 已经转换成矩阵了。 col* 函数已经在矩阵上运行。因此,如果您愿意,我们可以做dta[colMeans(sapply(dta, is.na)) &lt; .5],而且它仍然非常易读。在那个链接的问题中,filter 实际上是最简洁/矢量化/简单的解决方案,而不是一个异国情调的解决方案。但我猜你不是我农庄的地址。 @DavidArenburg 问题是关于在dplyr 管道中交付转换。我认为base 解决方案在这种情况下可能会更有效,但我的目标是在dplyr 管道 进行这种转换。这主要是出于个人偏好的动机,即在dplyr 范围内与其他转换一起进行这种转换,这主要导致代码演示和 R 脚本更适合我。我同意base 可以证明更有效,但是。我也喜欢@akrun 将exotic touch 添加到解决方案中的想法。 @DavidArenburg 我还可以补充一点,这种转换是更长的工作流程的一部分,包括最后的图表。实际上,这些转换仅用于生成特定更改的目的,因此撇开性能问题不谈,代码在 take master data -> do some things on it 行上读得更少是有意义的-> 生成图表。它只是很好,我会更简洁地阅读。正如通过教学,我们学习正确指出的那样,主要是关于工作流程而不是其他任何事情。在我的辩护中,我会说这在一定程度上是品味问题。

以上是关于有条件地选择 dplyr 中某些值比例为 NA 的列的主要内容,如果未能解决你的问题,请参考以下文章

R语言dplyr包na_if函数根据条件将数据对象替换为NA值实战

R语言dplyr包的mutate函数将列添加到dataframe中或者修改现有的数据列:基于条件判断创建布尔型指示变量将异常离散编码转化为NA值

使用dplyr将多个值设置为NA

使用dplyr将R中的所有NA值替换为0

使用 dplyr 按组将 NA 替换为上一个或下一个值

R语言dplyr包将dataframe中的NA值替换(replace)为0实战:所有NA值替换(replace)为0具体列的NA值替换(replace)为0若干列的NA值替换(replace)为0