dplyr: filter_ with character condition 不工作

dplyr: filter_ with character condition not working

这是我的数据:

df <- tibble::tribble(
  ~A,  ~B,  ~C,  ~D,
  2L, "a", "e", 2L,
  4L, "a", "f", NA_integer_,
  4L, "b", "g", NA_integer_,
  4L, "b", "h", NA_integer_
  )

df$B <- as.factor(df$B) 
df$A <- as.factor(as.character(df$A)) 

这里是我作为字符的过滤条件:

remove2 <- "as.integer(A)!=2L"

我只想删除 A==2 的观察结果,但下面的代码却保留了它,为什么?

df %>% dplyr::filter_(remove2)

我想使用 filter_,因为它接受条件作为字符。如果你可以建议过滤器(没有下划线版本)并以字符为条件,那也可以。

尝试以下操作:

remove2 <- "as.numeric(as.character(A))!=2L"

df %>% dplyr::filter_(remove2)

# A tibble: 3 x 4
  A     B     C         D
  <fct> <fct> <chr> <int>
1 4     a     f        NA
2 4     b     g        NA
3 4     b     h        NA

请注意,因素的编码方式不同。参见

 as.integer(df$A)
 [1] 1 2 2 2

要获取因子 "as shown" 的值,请使用 as.numeric(as.character(.))

其他答案指出下划线函数已被弃用(尽管它们仍然有效)。为了以一种绝对面向未来的方式实现这一点,使用简单的 base R:

可能是个好主意
df[which(df[["A"]] != 2L),]
# A tibble: 3 x 4
  A     B     C         D
  <fct> <fct> <chr> <int>
1 4     a     f        NA
2 4     b     g        NA
3 4     b     h        NA

其他人已经解释了这个问题的原因,即factor内部编码为整数,这可能与表面看起来不同。我想指出的另一件事是 filter_dplyr 0.7 以来已被弃用。所以我们可以考虑使用 filter 函数将字符串评估为以下两个选项。

remove2 <- "as.integer(as.character(A)) != 2L"

library(dplyr)
library(rlang)

df %>% filter(eval(parse(text = remove2)))
# # A tibble: 3 x 4
#   A     B     C         D
#   <fct> <fct> <chr> <int>
# 1 4     a     f        NA
# 2 4     b     g        NA
# 3 4     b     h        NA

df %>% filter(eval(parse_expr(remove2)))
# # A tibble: 3 x 4
#   A     B     C         D
#   <fct> <fct> <chr> <int>
# 1 4     a     f        NA
# 2 4     b     g        NA
# 3 4     b     h        NA

作为字符串的代码是一种反模式。这就提出了一个问题:字符串从哪里来?

如果是你,开发者,输入它,它既更难编写(你不会从自动完成等 IDE 功能中受益),也更容易出现错误(你可以编写在语法上无效的代码,这些代码在实际解析和评估之前不会被捕获,可能要晚得多,会引发更难理解的错误)。

如果不是您的用户输入,这是一个重大的安全漏洞。

你可以这样做:

remove2 <- quote(as.numeric(as.character(A)) != 2L)

filter(df, !! remove2)

!! 是 tidyeval 框架中的 "unquote" 运算符)。

虽然它也不完全令人满意(在我看来仍然是代码的味道),因为很少需要取消对整段代码的引用,通常它只是一个变量名。