我想从用户那里动态获取条件,所以我构建了一个闪亮的应用程序,可以从输入字段中获取它们。问题是as.formula
不适用于带有逗号的字符向量(没有效果)。
代码:
all_conditions =
"condition1 ~ 0,
condition2 ~ 1,
condition3 ~ 2"
my_dataset %>% group_by(id) %>%
summarise(FLAG = case_when(
as.formula(all_conditions) )
)
我得到:
评估错误:: 2:100:意外的','
我尝试使用paste
并成功将逗号转义。
答案 0 :(得分:4)
收集输入的方式不太实用。您的问题是您正在尝试解析如下所示的代码:
var1, var2, var3
尝试在R控制台中键入该错误,您将得到相同的错误:
#> Error: unexpected ',' in "var1,"
因此,首先重构代码,以便将输入收集为两个向量:
cnds <- c("condition1", "condition2", "condition3")
vals <- c("1", "2", "3")
现在,您有两种选择将这些字符串转换为R代码:解析或创建符号。当您期望使用任意R代码时,可以使用前者;而在期望变量或列名称时,可以使用后者。你能发现差异吗?
rlang::parse_exprs(c("foo", "bar()", "100"))
#> [[1]]
#> foo
#>
#> [[2]]
#> bar()
#>
#> [[3]]
#> [1] 100
rlang::syms(c("foo", "bar()", "100"))
#> [[1]]
#> foo
#>
#> [[2]]
#> `bar()`
#>
#> [[3]]
#> `100`
在您的情况下,您可能需要解析,因为条件将是R代码。因此,让我们首先解析两个向量:
cnds <- map(cnds, rlang::parse_expr)
vals <- map(vals, rlang::parse_expr)
我正在映射parse_expr()
而不是使用复数版本parse_exprs()
,因为后者可以返回比其输入更长的列表。例如,parse_exprs(c("foo; bar", "baz; bam"))
将2个字符串转换为4个表达式的列表。如果字符串包含多个表达式,parse_expr()
将返回错误,因此在我们的情况下更健壮。
现在,我们可以映射LHS和RHS的这两个列表并创建公式。一种简单的方法是使用准引用来创建公式,方法是将每个LHS及其对应的RHS取消引用:
fs <- map2(cnds, vals, function(c, v) rlang::expr(!!c ~ !!v))
结果是准备好可以拼接成case_when()
的公式表达式的列表:
data %>% mutate(result = case_when(!!!fs))
使用rlang::qq_show()
来准确查看未拼接的接头在做什么:
rlang::qq_show(mutate(result = case_when(!!!fs)))
#> mutate(result = case_when(condition1 ~ 1, condition2 ~2, condition3 ~ 3))
答案 1 :(得分:3)
借用@phiver的示例可以做到:
conditions <- "gear == 3 ~ 0, gear == 4 ~ 1, TRUE ~ 2"
mtcars %>% group_by(vs) %>%
mutate(FLAG = eval(parse(text=sprintf("case_when(%s)",conditions))))
# # A tibble: 32 x 12
# # Groups: vs [2]
# mpg cyl disp hp drat wt qsec vs am gear carb FLAG
# <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1 21.0 6 160.0 110 3.90 2.620 16.46 0 1 4 4 1
# 2 21.0 6 160.0 110 3.90 2.875 17.02 0 1 4 4 1
# 3 22.8 4 108.0 93 3.85 2.320 18.61 1 1 4 1 1
# 4 21.4 6 258.0 110 3.08 3.215 19.44 1 0 3 1 0
# 5 18.7 8 360.0 175 3.15 3.440 17.02 0 0 3 2 0
# 6 18.1 6 225.0 105 2.76 3.460 20.22 1 0 3 1 0
# 7 14.3 8 360.0 245 3.21 3.570 15.84 0 0 3 4 0
# 8 24.4 4 146.7 62 3.69 3.190 20.00 1 0 4 2 1
# 9 22.8 4 140.8 95 3.92 3.150 22.90 1 0 4 2 1
# 10 19.2 6 167.6 123 3.92 3.440 18.30 1 0 4 4 1
这里的想法是您不能单独评估字符串,因为它本身不是正确的语法,因此我们必须首先围绕它构建一个正确的调用(此处使用sprintf
),然后才能在飞(因此可以在适当的环境中进行评估,而无需其他技巧)。
答案 2 :(得分:1)
您需要将所有条件放在列表中,并使用定量和准引号(!!!)使其起作用。在您的代码示例之后,我将以mtcars为例。
library(dplyr)
# create list of quosures
conditions <- list(quo(gear == 3 ~ 0),
quo(gear == 4 ~ 1),
quo(TRUE ~ 2))
mtcars %>% group_by(vs) %>%
mutate(FLAG = case_when(!!! conditions)) # quasiquotation using !!! to splice the list
# A tibble: 32 x 12
# Groups: vs [2]
mpg cyl disp hp drat wt qsec vs am gear carb FLAG
<dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
1 21 6 160 110 3.9 2.62 16.5 0 1 4 4 1
2 21 6 160 110 3.9 2.88 17.0 0 1 4 4 1
3 22.8 4 108 93 3.85 2.32 18.6 1 1 4 1 1
4 21.4 6 258 110 3.08 3.22 19.4 1 0 3 1 0
5 18.7 8 360 175 3.15 3.44 17.0 0 0 3 2 0
6 18.1 6 225 105 2.76 3.46 20.2 1 0 3 1 0
7 14.3 8 360 245 3.21 3.57 15.8 0 0 3 4 0
8 24.4 4 147. 62 3.69 3.19 20 1 0 4 2 1
9 22.8 4 141. 95 3.92 3.15 22.9 1 0 4 2 1
10 19.2 6 168. 123 3.92 3.44 18.3 1 0 4 4 1
# ... with 22 more rows