7

I'd like to get conditions dynamically from the user, so I built a shiny app that gets them from an input field. Problem is that as.formula doesn't work for a character vector with a comma (without it works fine).

Code:

all_conditions = 
  "condition1 ~ 0,
   condition2 ~ 1,
   condition3 ~ 2"

 my_dataset %>% group_by(id) %>%
  summarise(FLAG = case_when(
      as.formula(all_conditions) )
   )

I get:

Evaluation error: :2:100: unexpected ','

I have tried using paste and escaping the comma with no success.

4

3 回答 3

7

您收集输入的方式不是很实用。您的问题是您正在尝试解析如下所示的代码:

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 列表并创建公式。一种简单的方法是使用 quasiquotation 通过取消引用每个 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))
于 2018-08-09T10:27:19.460 回答
4

借用@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)然后我们可以动态评估它(所以它是在正确的无需更多技巧的环境)。

于 2018-08-09T10:09:10.070 回答
1

您需要将每个条件都放在一个列表中,并使用 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
于 2018-08-09T09:41:30.330 回答