【问题标题】:dplyr: filter_ with character condition not workingdplyr:带有字符条件的 filter_ 不起作用
【发布时间】:2018-05-19 10:17:49
【问题描述】:

这是我的数据:

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_ 因为它接受条件作为字符。如果您可以建议过滤器(不带下划线版本)并将字符作为条件,那也可以。

【问题讨论】:

  • 问题出在过滤器的 as.integer 部分。如果您执行 as.integer(df$A) ,则返回值为 1、2、2、2。而不是您期望的 2、4、4、4。见this SO post
  • 我猜其他答案 cmets 间接说明了这一点 - 我可能会首先尝试避免因素。为什么不简单地将变量保存为字符/整数?您可以随时根据需要进行临时分解
  • 出于好奇,为什么要将条件作为角色?您是否打算将此链接到某些用户输入或类似的内容?
  • @phiver 我不知道!很高兴知道。谢谢!
  • @camille 我认为 filter_ 会接受引用的表达式并解决问题。

标签: r dplyr tidyverse rlang


【解决方案1】:

其他人已经解释了导致此问题的原因,即 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

【讨论】:

  • 我不是反对者(实际上我赞成)。只是我的 2cts: code as string 似乎是一种反模式。我会选择:remove2 &lt;- rlang::expr(as.numeric(as.character(A)) != 2L) ; filter(df, !! remove2)
  • (或用quote代替rlang::expr
  • @Aurèle 您介意发布带有rlang 选项的答案吗?我正在尝试解决这个问题,并且也对如何从字符串中的操作开始执行此操作感兴趣,因为 OP 有
  • @camille 当然,stackoverflow.com/a/50428178/6197649 。我不推荐字符串中的代码,原因在我的回答中详述。虽然如果我们要这样做,我会从 www 得到这个答案
  • @www eval(parse_expr.. 对我来说简直是优雅而成熟的!!
【解决方案2】:

作为字符串的代码是一种反模式。它提出了一个问题:字符串从何而来?

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

如果输入来自不是您的用户,则这是一个主要的安全漏洞。

你可以这样做:

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

filter(df, !! remove2)

!! 是 tidyeval 框架中的“取消引用”运算符)。

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

【讨论】:

  • 我认为 filter_ 会接受带引号的表达式并解决问题,但是这个引号 unquote 很好地避免了这种情况。
【解决方案3】:

尝试以下方法:

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.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

【讨论】:

  • 正如@Aurèle 在下面指出的,dplyr 函数的下划线版本已被弃用,有利于基于 NSE/rlang 的操作
  • @coffeinjunky 基本解决方案很有趣!很高兴知道这一点。
猜你喜欢
  • 2019-09-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-03-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多