具有多列条件的dplyr过滤器

7cjasjjr  于 2023-06-03  发布在  其他
关注(0)|答案(6)|浏览(727)

我想从数据框中删除与特定变量组合相对应的行。
这里有一个虚拟数据:

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

我的问题是:我有很多列,比如grand father,uncle1,uncle2,uncle3,我想避免这样的事情:

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)

slmsl1lt

slmsl1lt1#

一个可能的dplyr(0.5.0.9004 <= version < 1.0)解决方案是:

# > 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):选择除fathermother之外的所有列。
  • all_vars(is.na(.)):保留is.naTRUE的行,用于所有所选列。

注意:如果要保留is.naTRUE的行,则应使用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")

yk9xbfzb

yk9xbfzb2#

dplyr >= 1.0.4

如果你使用的是dplyr version >= 1.0.4,你真的应该使用if_anyif_all,它专门将 predicate 函数的结果组合到一个逻辑向量中,使其在filter中非常有用。语法与across相同,但添加了以下动词以帮助满足此需求:if_any/if_all

library(dplyr)

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

这里我已经写出了变量名,但是你可以使用任何tidy selection helper来指定变量(例如,按名称或位置的列范围,正则表达式匹配,子字符串匹配,开始/结束等)。

输出

father mother children cousins
1      1      1       NA      NA
omhiaaxx

omhiaaxx3#

没有一个答案似乎是一个适应性强的解决方案。我认为这样做的目的不是列出所有的变量和值来过滤数据。
实现这一点的一个简单方法是合并。如果你有df_filter中的所有条件,那么你可以这样做:

df_results = df_filter %>% left_join(df_all)
zwghvu4y

zwghvu4y4#

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,]
fykwrbwg

fykwrbwg5#

这里是一个使用两个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连续接受提供的变量并执行逻辑检查。这两个结果用&连接。可以调整Reduce函数的第二个参数,以包含您想要的data.frame中的任何变量。
然后使用逻辑向量进行子集

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

pbossiut6#

这个答案基于@Feng Jiang的答案,使用dplyr::left_joint()操作,更像是一个reprex。此外,它确保在df_filter中的变量顺序与原始数据集中的变量顺序不同的情况下恢复列的正确顺序。此外,针对重复组合扩展数据集,以显示这些是过滤输出(df_out)的一部分。

library(dplyr)

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

dataset <- data.frame(father, mother, children, cousins) 
df_filter <- data.frame( father = 1, mother = 1, children = NA, cousins = NA)
                         
test  <- df_filter  %>% 
  left_join(dataset) %>% 
  relocate(colnames(dataset))

相关问题