在用户定义的函数中使用胶水进行变异

Mutate using glue in a user defined function

我想使用 glue 函数更改特定列中的值以包含来自另一列的信息。

我通常是这样做的:

library(glue)
library(dplyr)
df = data.frame(x = c("Banana","Apple","Melon"),
                y = c(10,15,27),
                z = rep(c("something_else"),3))
df %>%
  mutate(x = glue("{x} ({y})"))

输出:

#>             x  y              z
#> 1 Banana (10) 10 something_else
#> 2  Apple (15) 15 something_else
#> 3  Melon (27) 27 something_else

当我尝试使用数据框和列名作为用户定义函数的输入来做同样的事情时,我的问题出现了。

我最初的想法是使用双花括号结合胶水函数来传递输入,但这会导致错误。

concatenate_value_to_string <- function(tbl,var1,var2) {
  tbl %>%
    mutate({{var1}} := glue("{{{var1}}} ({{{var2}}})"))
}

concatenate_value_to_string(df,x,y)
#> Error in UseMethod("mutate"): no applicable method for 'mutate' applied to an object of class "function"

reprex package (v2.0.0)

于 2021-08-02 创建

显然三花括号不是这里的解决方案,有人可以帮我吗?

谢谢。

另一个选项可以是:

concatenate_value_to_string <- function(tbl, var1, var2) {
    tbl %>%
        mutate(!!var1 := glue("{.data[[var1]]} ({.data[[var2]]})")) 
}

concatenate_value_to_string(df, "x", "y")

            x  y              z
1 Banana (10) 10 something_else
2  Apple (15) 15 something_else
3  Melon (27) 27 something_else

当您想传递参数名称而不是字符串时,这是另一个选项。函数 enquo 用于扩散用户定义的变量,而不是 ensym which returns 一个原始表达式 enquo returns 一个 quosure,它是一个绑定到的表达式一个环境。所以我们使用 get_expr 来访问它的表达式,并用 rlang::eval_tidybase::eval 包装它以在上下文中进行评估。

library(rlang)

concatenate_value_to_string <- function(tbl, var1, var2) {
  tbl %>%
    mutate(!!enquo(var1) := glue("{eval_tidy(get_expr(enquo(var1)))} ({eval_tidy(get_expr(enquo(var2)))})"))
}

concatenate_value_to_string(df, x, y)
            x  y              z
1 Banana (10) 10 something_else
2  Apple (15) 15 something_else
3  Melon (27) 27 something_else

你可以使用 dplyr::pull():

concatenate_value_to_string <- function(tbl,var1,var2) {
  tbl %>%
    mutate({{var1}} :=  glue("{pull(., {{var1}})} ({pull(., {{var2}})})"))
}

concatenate_value_to_string(df,x,y)

concatenate_value_to_string(df,x,y)
#>             x  y              z
#> 1 Banana (10) 10 something_else
#> 2  Apple (15) 15 something_else
#> 3  Melon (27) 27 something_else

eval(rlang::expr()),我们首先从输入构建符号,然后在数据框的上下文中对其进行评估。

concatenate_value_to_string <- function(tbl,var1,var2) {
  tbl %>%
    mutate({{var1}} :=  glue("{eval(expr({{var1}}))} ({eval(expr({{var2}}))})"))
}

concatenate_value_to_string(df,x,y)
#>             x  y              z
#> 1 Banana (10) 10 something_else
#> 2  Apple (15) 15 something_else
#> 3  Melon (27) 27 something_else

您尝试的方法不起作用,因为 mutate() 不会替换 {{foo}} 如果它是字符串的一部分,在上述解决方案中,pull()expr()做吧。

在这种情况下,我个人更愿意使用 sprintf()

concatenate_value_to_string <- function(tbl,var1,var2) {
  tbl %>%
    mutate({{var1}} :=  sprintf("%s (%s)", {{var1}}, {{var2}}))
}

concatenate_value_to_string(df,x,y)
#>             x  y              z
#> 1 Banana (10) 10 something_else
#> 2  Apple (15) 15 something_else
#> 3  Melon (27) 27 something_else