使用 as.formula 和逗号
Using as.formula with a comma
我想从用户那里动态获取条件,所以我构建了一个闪亮的应用程序,从输入字段中获取它们。问题是 as.formula
不适用于带逗号的字符向量(没有它可以正常工作)。
代码:
all_conditions =
"condition1 ~ 0,
condition2 ~ 1,
condition3 ~ 2"
my_dataset %>% group_by(id) %>%
summarise(FLAG = case_when(
as.formula(all_conditions) )
)
我得到:
Evaluation error: :2:100: unexpected ','
我试过使用 paste
并转义逗号但没有成功。
您需要将每个条件都放在一个列表中,并使用 quosures 和 quasiquotation (!!!) 来让它工作。按照您的代码示例,我将以 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
借用@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
)然后我们可以评估它即时(因此它在正确的环境中进行评估,无需进一步的技巧)。
您收集输入的方式不太实用。您的问题是您正在尝试解析如下代码:
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()
因为后者可以 return 一个比其输入长的列表。例如 parse_exprs(c("foo; bar", "baz; bam"))
将 2 个字符串转换为 4 个表达式的列表。 parse_expr()
return如果一个字符串包含多个表达式则出错,因此在我们的例子中更加稳健。
现在我们可以映射这两个 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))
我想从用户那里动态获取条件,所以我构建了一个闪亮的应用程序,从输入字段中获取它们。问题是 as.formula
不适用于带逗号的字符向量(没有它可以正常工作)。
代码:
all_conditions =
"condition1 ~ 0,
condition2 ~ 1,
condition3 ~ 2"
my_dataset %>% group_by(id) %>%
summarise(FLAG = case_when(
as.formula(all_conditions) )
)
我得到:
Evaluation error: :2:100: unexpected ','
我试过使用 paste
并转义逗号但没有成功。
您需要将每个条件都放在一个列表中,并使用 quosures 和 quasiquotation (!!!) 来让它工作。按照您的代码示例,我将以 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
借用@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
)然后我们可以评估它即时(因此它在正确的环境中进行评估,无需进一步的技巧)。
您收集输入的方式不太实用。您的问题是您正在尝试解析如下代码:
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()
因为后者可以 return 一个比其输入长的列表。例如 parse_exprs(c("foo; bar", "baz; bam"))
将 2 个字符串转换为 4 个表达式的列表。 parse_expr()
return如果一个字符串包含多个表达式则出错,因此在我们的例子中更加稳健。
现在我们可以映射这两个 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))