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" 运算符)。
虽然它也不完全令人满意(在我看来仍然是代码的味道),因为很少需要取消对整段代码的引用,通常它只是一个变量名。
这是我的数据:
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" 运算符)。
虽然它也不完全令人满意(在我看来仍然是代码的味道),因为很少需要取消对整段代码的引用,通常它只是一个变量名。