dplyr如何使用多个条件过滤多个变量(列)?

4
我计划使用一行代码来过滤多个列中的数据,以减少运行代码所需的时间。以下是我用于测试代码的示例数据。基本上,我想要删除任何包含0、1、2和NA的行。
test <- data.frame(A = c(1,0,2,3,4,0,5,6,0,7,0,8,0,9,NA),
                 B = c(0,1,0,2,3,4,0,5,0,7,8,0,NA,9,0),
                 C = c(1,2,3,0,0,4,5,6,0,7,0,8,NA,0,9))

我用以下代码清理数据。尽管它完成了任务,但这段代码非常繁琐,在运行大型数据库时需要花费相当长的时间。
test %>%  filter(!is.na(A)) %>%
filter(!is.na(B)) %>%
filter(!is.na(C)) %>% 
filter(A != 0) %>% 
filter(A != 1) %>%
filter(A != 2) %>% 
filter(B != 0) %>% 
filter(B != 1) %>%
filter(B != 2) %>%
filter(C != 0) %>% 
filter(C != 1) %>%
filter(C != 2)

  A B C
1 6 5 6
2 7 7 7

我试图使用 filterfilter_atany_vars 来简化代码,但这并没有起作用。

以下是我尝试解决此问题的代码(所有这些代码都无法删除包含0(或1、2和NA)的行):

df_total <- test %>%
filter_at(vars(A, B, C), any_vars(!is.na(.))) %>%
filter_at(vars(A, B, C), any_vars(. != 2)) %>%
filter_at(vars(A, B, C), any_vars(. != 1)) %>%
filter_at(vars(A, B, C), any_vars(. != 0))

df_total <- test %>%
filter_at(vars(A, B, C), any_vars(!is.na(.) | . != 2 | . != 1 | . != 0))

df_total <- test %>%
filter(!is.na(A) | A!= 2 | A!= 1 | A!= 0) %>%
filter(!is.na(B) | B!= 2 | B!= 1 | B!= 0) %>%
filter(!is.na(C) | C!= 2 | C!= 1 | C!= 0) %>%

我在这里弄不清楚我的错误出在哪里。我一直在文档和R之间反复寻找解决方法,但我的努力都是无用的。您能否建议我在代码中做错了什么?如何在一行代码中为多个列编写具有多个条件的代码?一行代码的目的是加快R的运行速度。任何建议/建议/资源都将不胜感激!谢谢。

2个回答

3

另一个可能的解决方案:

library(dplyr)

test %>% 
  filter(complete.cases(.) & if_all(everything(), ~ !(.x %in% 0:2)))

#>   A B C
#> 1 6 5 6
#> 2 7 7 7

嗨,PaulS,感谢您的回答。我能请您解释一下吗?我的数据库中不仅有A、B和C,实际上我有近400个变量,我想基于这三列进行过滤。我认为在这种情况下使用_everything()_并不合适。我该如何调整您的代码以解决这个问题? - Kob
1
@Kob:我猜以下代码可以运行:test %>% filter(complete.cases(.[c("A", "B", "C")]) & if_all(c(A, B, C), ~ !(.x %in% 0:2))) - PaulS
它完美地工作了。请问一下这段代码中某些点的含义,好吗?我的第一个问题是,为什么在_complete.cases_后面加上**.**?另一个问题是,为什么使用if_all而不是if_any?我的理解是,if_all将产生集合之间的交集。换句话说,如果这三列包含0(或1和2),则该行将被删除。显然,我的理解是错误的。你能给我解释一下吗? - Kob
1
@Kob:Q1:点号表示来自前一个管道(在本例中是数据框“test”)的输出。Q2:使用以下filter(A != 0) %>% filter(A != 1) %>% filter(A != 2),您的意思是删除所有包含0或1或2的行。因此,可以将其转换为filter(!A %in% 0:2)。由于还必须满足列B和C的这个条件,因此需要一个“and”:filter((!A %in% 0:2) & (!B %in% 0:2) & (!C %in% 0:2)) - PaulS
1
这就是我昨天整天在寻找的东西。非常感谢您的澄清!!!对于像我这样的新手来说,理解函数非常(非常)有帮助。我感谢您花时间解释所有这些内容。 - Kob

2
test %>% 
    filter(across(c(A, B, C), function(x) !is.na(x) & !x %in% c(0, 1, 2)))

# A B C
# 6 5 6
# 7 7 7

感谢您的回答@Adromine。如果我错了,请纠正我。在当前版本的dplyr中,across函数已经被弃用,建议我们改用if_any或if_all,是这样吗?我使用across函数成功运行了代码,然后我决定更改,因为它已经被弃用了。我尝试在我的代码中使用if_any,但它没有起作用。 - Kob
1
across 并未被弃用。mutatesummarise 等函数的 _at_all 变体已被弃用。目前推荐使用 if_anyif_all,但它们仅适用于 filter 中的使用。您可以在上面的代码中将 across 替换为 if_all。您可以在 帮助页面 上了解更多关于 acrossif_any/all 的信息。 - AdroMine
我觉得我完全误解了这些函数的折旧。非常感谢您的澄清。我之前已经阅读过这个帮助页面,但是我仍然对if_any/all有疑问。据我理解,if_all将产生集合的交互,并且if_any将产生集合的并集。在我的情况下,我希望删除任何包含0(或1和2)的行,从A、B和C的任何列中删除。这就是为什么我选择使用if_any而不是if_all。但它并没有像我想象的那样工作。我在这里误解了什么吗?任何建议都将不胜感激! - Kob
1
filter(if_all(cols = c(A, B, C), .fns = fun1)) is equivalent to filter(fun1(A) & fun(B) & fun(C)) while filter(if_any(cols = c(A, B, C), .fns = fun1)) is same as filter(fun1(A) | fun(B) | fun(C)) - AdroMine

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接