如何在 dplyr::mutate 中使用 R 中的表达式

How to use an expression in dplyr::mutate in R

我想根据给定的字符向量添加一个新列。 例如,在下面的示例中,我想添加在 expr:

中定义的列 d
library(magrittr)

data <- tibble::tibble(
  a = c(1, 2),
  b = c(3, 4)
)

expr <- "d = a + b"

如下:

data %>%
  dplyr::mutate(d = a + b)

# # A tibble: 2 x 3
#       a     b     d
#   <dbl> <dbl> <dbl>
# 1     1     3     4
# 2     2     4     6

但是,在下面的代码中,虽然计算本身(即添加)有效,但新列的名称与我预期的不同。

data %>%
  dplyr::mutate(!!rlang::parse_expr(expr))

# # A tibble: 2 x 3
#       a     b `d = a + b`
#   <dbl> <dbl>       <dbl>
# 1     1     3           4
# 2     2     4           6

data %>%
  dplyr::mutate(!!rlang::parse_quo(expr, env = rlang::global_env()))

# # A tibble: 2 x 3
#       a     b `d = a + b`
#   <dbl> <dbl>       <dbl>
# 1     1     3           4
# 2     2     4           6

data %>%
  dplyr::mutate(rlang::eval_tidy(rlang::parse_expr(expr)))

# # A tibble: 2 x 3
#       a     b `rlang::eval_tidy(rlang::parse_expr(expr))`
#   <dbl> <dbl>                                       <dbl>
# 1     1     3                                           4
# 2     2     4                                           6

如何正确使用 dplyr::mutate 中的表达式?

我的问题类似于,但在我的示例中,新变量 (d) 及其定义 (a + b) 在单个字符向量 (expr).

要为变异列获取所需的名称,您仍然可以使用相同的语法并将结果分配给具有首选名称的列。要获得此名称,您可以使用正则表达式查找 = 之前的内容,然后删除可能存在的任何前导或尾随空格。

expr <- "x = a * b"
col_name <- trimws(str_extract(expr,"[^=]+"))

data %>%
   dplyr::mutate(!!col_name := !!rlang::parse_expr(expr))
# A tibble: 2 × 3
      a     b     x
  <dbl> <dbl> <dbl>
1     1     3     3
2     2     4     8

data %>%
   dplyr::mutate(!!col_name := !!rlang::parse_quo(expr, env = rlang::global_env()))
# A tibble: 2 × 3
      a     b     x
  <dbl> <dbl> <dbl>
1     1     3     3
2     2     4     8
 
data %>%
   dplyr::mutate(!!col_name := rlang::eval_tidy(rlang::parse_expr(expr)))
# A tibble: 2 × 3
      a     b     x
  <dbl> <dbl> <dbl>
1     1     3     3
2     2     4     8

让我们先看看dplyr::mutate需要什么样的表达式来创建命名变量:我们需要一个包含表达式的命名列表,以根据给定列表元素名称的表达式创建变量。

library(tidyverse)

data <- tibble::tibble(
  a = c(1, 2),
  b = c(3, 4)
)

expr <- "d = a + b"
# let's rewrite the string above as named list containing an expression.
expr2 <- list(d = expr(a + b))

# this works as expected:
data %>% 
  mutate(!!! expr2)

#> # A tibble: 2 x 3
#>       a     b     d
#>   <dbl> <dbl> <dbl>
#> 1     1     3     4
#> 2     2     4     6

现在我们只需要一个函数将字符串转换为命名列表,其中包含等式 right-hand 边的表达式。该名称需要是等式的 left-hand 端。我们可以通过常规的字符串操作来做到这一点。最后,我们需要将等式的 right-hand 边从字符串转换为表达式。我们可以在这里使用基数 R 的 str2lang

create_expr_ls <- function(str_expr) {
  expr_nm <- str_extract(str_expr, "^\w+")
  expr_code <- str_replace_all(str_expr, "(^\w+\s?=\s?)(.*)", "\2")
  set_names(list(str2lang(expr_code)), expr_nm)
}

expr3 <- create_expr_ls(expr)

data %>% 
  mutate(!!! expr3)

#> # A tibble: 2 x 3
#>       a     b     d
#>   <dbl> <dbl> <dbl>
#> 1     1     3     4
#> 2     2     4     6

reprex package (v0.3.0)

于 2022-01-23 创建

这些工作中的任何一个。第二个与第一个类似,但不要求 rlang 在搜索路径上。如果 d= 部分不存在于 expr 中,则第三个和第四个也有效,在这种情况下使用默认名称。最后一个只用了base R,也是最短的

data %>% mutate(within(., !!parse_expr(expr)))

data %>% mutate(within(., !!parse(text = expr)))

data %>% mutate(data, !!parse_expr(sprintf("tibble(%s)", expr)))

data %>% { eval_tidy(parse_expr(sprintf("mutate(., %s)", expr))) }

within(data, eval(parse(text = expr)))  # base R

备注

假设这个前提:

library(dplyr)
library(rlang)

# input
data <- tibble(a = c(1, 2), b = c(3, 4))
expr <- "d = a + b"