【问题标题】:dplyr filter with condition on multiple columns具有多列条件的 dplyr 过滤器
【发布时间】:2017-10-11 20:10:09
【问题描述】:

这是一个虚拟数据:

father<- c(1, 1, 1, 1, 1)
mother<- c(1, 1, 1, NA, NA) 
children <- c(NA, NA, 2, 5, 2) 
cousins   <- c(NA, 5, 1, 1, 4) 


dataset <- data.frame(father, mother, children, cousins)  
dataset


father  mother  children cousins
1      1       NA      NA
1      1       NA       5
1      1        2       1
1     NA        5       1
1     NA        2       4

我想过滤这一行:

  father  mother  children cousins
    1      1       NA      NA

我可以做到:

test <- dataset %>% 
filter(father==1 & mother==1) %>%
filter (is.na(children)) %>%
filter (is.na(cousins))
test  

我的问题: 我有很多列,例如祖父、叔叔1、叔叔2、叔叔3,我想避免这样的事情:

  filter (is.na(children)) %>%
  filter (is.na(cousins)) %>%
  filter (is.na(uncle1)) %>%
  filter (is.na(uncle2)) %>%
  filter (is.na(uncle3)) 
  and so on...

我如何使用 dplyr 过滤所有带有 na 的列(父亲==1 和母亲==1 除外)

【问题讨论】:

标签: r dplyr


【解决方案1】:

dplyr >= 1.0.4

如果您使用 dplyr 版本 >= 1.0.4,您确实应该使用 if_any 或 if_all,它们专门将谓词函数的结果组合成一个逻辑向量,使其在 filter 中非常有用。语法与across 相同,但添加了这些动词来帮助满足这一需求:if_any/if_all。

library(dplyr)

dataset %>% 
  filter(if_all(-c(father, mother), ~ is.na(.)), if_all(c(father, mother), ~ !is.na(.)))

输出

  father mother children cousins
1      1      1       NA      NA

【讨论】:

    【解决方案2】:

    一个可能的dplyr(0.5.0.9004

    # > packageVersion('dplyr')
    # [1] ‘0.5.0.9004’
    
    dataset %>%
        filter(!is.na(father), !is.na(mother)) %>%
        filter_at(vars(-father, -mother), all_vars(is.na(.)))
    

    解释:

    • vars(-father, -mother):选择除father 和mother 之外的所有列。
    • all_vars(is.na(.)):为所有选定的列保留is.na 为TRUE 的行。

    注意:如果 any 列中is.na 为TRUE 的行要保留,则应使用any_vars 而不是all_vars。


    更新(2020-11-28)

    由于 _at 函数和 vars 自 dplyr 1.0 以来已被 across 的使用所取代,现在建议使用以下方式(或类似方式):

    dataset %>%
        filter(across(c(father, mother), ~ !is.na(.x))) %>%
        filter(across(c(-father, -mother), is.na))
    

    在此处查看across 的更多示例以及如何使用新方法重写以前的代码:Colomn-wise operatons 或在安装最新版本的dplyr 后在 R 中键入 vignette("colwise")。

    【讨论】:

      【解决方案3】:

      似乎没有一个答案是适应性强的解决方案。我认为目的不是列出所有变量和值来过滤数据。

      实现这一目标的一种简单方法是通过合并。如果你有 df_filter 中的所有条件,那么你可以这样做:

      df_results = df_filter %>% left_join(df_all)
      

      【讨论】:

      • 不清楚“df_filter 中的所有条件”是什么意思。我会怀疑 dplyr 代码中的所有条件,但您的代码示例另有说明。请澄清
      • 表示所有单元格都满足条件。就像“我想过滤这一行:”下的问题中给出的示例一样。
      【解决方案4】:

      dplyr 解决方案:

      test <- dataset %>% 
        filter(father==1 & mother==1 & rowSums(is.na(.[,3:4]))==2)
      

      其中“2”是应为NA 的列数。

      这给出了:

      > test
        father mother children cousins
      1      1      1       NA      NA
      

      您也可以在基础 R 中应用此逻辑:

      dataset[dataset$father==1 & dataset$mother==1 & rowSums(is.na(dataset[,3:4]))==2,]
      

      【讨论】:

        【解决方案5】:

        这是一个使用两个 Reduce 函数和 [ 子集的基本 R 方法。

        keepers <- Reduce(function(x, y) x == 1 & y == 1, dataset[, 1:2]) &
                   Reduce(function(x, y) is.na(x) & is.na(y), dataset[, 3:4])
        keepers
        [1]  TRUE FALSE FALSE FALSE FALSE
        

        每个Reduce 连续获取提供的变量并执行逻辑检查。这两个结果与&amp; 相关联。 Reduce 函数的第二个参数可以调整为在 data.frame 中包含您想要的任何变量。

        然后使用逻辑向量进行子集化

        dataset[keepers,]
          father mother children cousins
        1      1      1       NA      NA
        

        【讨论】:

        • 非常感谢。我正在寻找 dplyr
        猜你喜欢
        • 2020-04-17
        • 2019-10-10
        • 1970-01-01
        • 2017-05-06
        • 1970-01-01
        • 2022-01-19
        • 2013-01-18
        • 1970-01-01
        • 2022-11-18
        相关资源
        最近更新 更多