使用带逗号的as.formula

时间:2018-08-09 08:43:37

标签: r dplyr tidyeval

我想从用户那里动态获取条件,所以我构建了一个闪亮的应用程序,可以从输入字段中获取它们。问题是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并成功将逗号转义。

3 个答案:

答案 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