使用tidyverse进行条件过滤

时间:2017-09-12 14:04:21

标签: r dplyr tidyverse

我想根据可能存在或不存在的变量过滤我的数据框。作为预期的输出,我想要一个被过滤的df(如果它有过滤器变量),或者原始的,未过滤的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内?

2 个答案:

答案 0 :(得分:5)

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

此Q&amp; A包含最初的想法: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

答案 1 :(得分: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