【问题标题】:Conditional filtering using tidyverse使用 tidyverse 进行条件过滤
【发布时间】:2018-02-21 01:03:38
【问题描述】:

我想根据可能存在或不存在的变量过滤我的数据框。作为预期输出,我想要一个经过过滤的 df(如果它具有 filter 变量),或者是原始的、未过滤的 df(如果该变量丢失)。

这是一个最小的例子:

library(tidyverse)
df1 <- 
tribble(~a,~b,
        1L,"a",
        0L, "a",
        0L,"b",
        1L, "b")
df2 <- select(df1, b)

df1 进行过滤会返回所需的结果,即过滤后的小标题。

filter(df1, a == 1)
# A tibble: 2 x 2
      a     b
  <int> <chr>
1     1     a
2     1     b

但第二个抛出错误(预期),因为变量不在 df 中。

filter(df2, a == 1)
Error in filter_impl(.data, quo) : 
  Evaluation error: object 'a' not found.

我尝试了filter_at,这将是一个显而易见的选择,但如果没有与困境匹配的变量,则会引发错误。

filter_at(df2, vars(matches("a")), any_vars(. == 1L))    
Error: `.predicate` has no matching columns

所以,我的问题是:有没有办法创建一个条件过滤来产生预期的结果,最好是在 tidyverse 中?

【问题讨论】:

  • 我认为这个问答stackoverflow.com/questions/44001722/…应该回答你的问题
  • 预期结果是什么?
  • 例如(在链接的 Q 中),您可以执行 df2 %&gt;% filter(if("a" %in% names(.)) a == 1 else TRUE)df2 %&gt;% {if("a" %in% names(.)) filter(., a == 1) else .} 之类的操作
  • @JanvanderLaan OP 想要返回原始数据,如果变量不存在(“原始的、未过滤的 df(如果变量丢失)。”)
  • 你不能把它包装在trytryCatch 中吗?或者您希望能够在管道链中使用它?

标签: r dplyr tidyverse


【解决方案1】:

这样的?

# function for expected output
foo <- function(x, y){
  tmp <- which(colnames(x) %in% y)
  if(length(tmp) > 0){
    filter(x, select(x, tmp) == 1)
  }else{
    df1
  }
}

# run the functions
foo(df1, "a")
foo(df2, "a")
# or

df1 %>% foo("a")
# A tibble: 2 x 2
      a     b
  <int> <chr>
1     1     a
2     1     b

df2 %>% foo("a")
# A tibble: 4 x 2
      a     b
  <int> <chr>
1     1     a
2     0     a
3     0     b
4     1     b

【讨论】:

  • 谢谢,这是一个很好的答案,但我正在寻找一个 tidyverse 解决方案。
【解决方案2】:

正如@docendo-discimus 在 cmets 中指出的那样,以下解决方案有效。我还添加了rlang::has_name 而不是"a" %in% names(.)

此问答包含原始想法:Conditionally apply pipeline step depending on external value

df1 %>% 
   filter(if(has_name("a")) a == 1 else TRUE)
# A tibble: 2 x 2
      a     b
  <int> <chr>
1     1     a
2     1     b

df2 %>% 
   filter(if(has_name("a")) a == 1 else TRUE)
# A tibble: 4 x 1
      b
  <chr>
1     a
2     a
3     b
4     b

或者,使用{}

df1 %>%
  {if(has_name("a")) filter(., a == 1L) else .} 
# A tibble: 2 x 2
      a     b
  <int> <chr>
1     1     a
2     1     b

> df2 %>%
+   {if(has_name("a")) filter(., a == 1L) else .}
# A tibble: 4 x 1
      b
  <chr>
1     a
2     a
3     b
4     b

【讨论】:

    猜你喜欢
    • 2020-09-22
    • 1970-01-01
    • 2016-07-14
    • 1970-01-01
    • 2021-02-25
    • 1970-01-01
    • 2020-05-25
    • 2021-06-03
    • 2020-05-24
    相关资源
    最近更新 更多