R中的quo_name等价于quos。

9

你好,在阅读 dplyr 编程 中,我注意到可以使用 quo_name 添加名称。我想知道如何为多个列实现这样的操作,例如类似于 quos_name。例如:

my_mutate <- function(df, expr) {
  expr <- enquo(expr)
  mean_name <- paste0("mean_", quo_name(expr))
  sum_name <- paste0("sum_", quo_name(expr))
  
  mutate(df, 
    !!mean_name := mean(!!expr), 
    !!sum_name := sum(!!expr)
  )
}

变得

my_mutate <- function(df, ...) {
  exprs <-quos(...)
  mean_names <- paste0("mean_", quos_name(exprs))
  sum_names <- paste0("sum_", quos_name(exprs))
  
  mutate(df, 
    !!!mean_names := mean(!!!exprs), 
    !!!sum_names := sum(!!!exprs)
  )
}

例如,在所有指定的列中添加平均值和总和列... 当然,这只是一个例子,quo_names不存在。如果有一种方法可以做到这一点,那将非常有帮助。

我知道在data.table中可以做到这样的事情,例如DT[,(Col_names):=lapply(Cols,mean)](此代码不起作用,但我以前做过类似的事情)。


1
使用mutate_at怎么样?函数名称将作为后缀添加,而不是前缀,但其余部分将完成相同的工作。 - aosmith
很遗憾,这会覆盖在{.vars}中指定的列,我想要创建一个新的列。 - Geyer Bisschoff
不用在意之前的评论,我发现如果你给.funs参数命名,它会添加一个新列。 - Geyer Bisschoff
1
我认为你所描述的东西不存在,但除了@aomith的评论之外,你还可以在引用名称的向量上进行map操作,例如quos(a,b) %>% purrr::map(~my_mutate(df, !!.x)) - shayaa
2个回答

4

免责声明:虽然 @aosmith 提出的 mutate_at 在我看来是最好和最简单的解决方案,但如果没有 mutate_at,我认为使用 rlang 工具来解决问题可能会更有启发性。为了科学!

如评论所述,您需要查看 purrr::map() 函数系列。您还将遇到另一个问题,即 !!!mean_names := mean(!!!exprs),因为不能在赋值语句的左侧使用 !!! 拼接运算符。

最佳的 rlang 方法是将您的 mutate 表达式组合成一个命名列表。使用 quo 进行表达式算术和 stringr::str_c(或像您一直在使用的 paste0)进行字符串算术:

library( tidyverse )

my_mutate <- function(df, ...) {
  exprs <- enquos(...)

  mean_exprs <- set_names(
    map(exprs, ~quo(mean(!!.x))),               # mpg becomes mean(mpg)
    str_c("mean_", map_chr(exprs, quo_name)) )  # mpg becomes "mean_mpg"

  sum_exprs <- set_names(
    map(exprs, ~quo(sum(!!.x))),                # mpg becomes sum(mpg)
    str_c("sum_", map_chr(exprs, quo_name)) )   # mpg becomes "sum_mpg"

  mutate(df, !!!mean_exprs, !!!sum_exprs)
}

mtcars %>% my_mutate( mpg, cyl )
#    mpg cyl disp  hp ... mean_mpg mean_cyl sum_mpg sum_cyl
# 1 21.0   6  160 110 ... 20.09062   6.1875   642.9     198
# 2 21.0   6  160 110 ... 20.09062   6.1875   642.9     198
# 3 22.8   4  108  93 ... 20.09062   6.1875   642.9     198
# 4 21.4   6  258 110 ... 20.09062   6.1875   642.9     198
奖励:您会注意到,在我们上面的表达式定义中,我们重复了一段代码。我们可以将其提取到一个独立的函数中,该函数使用提供的函数自动构建表达式,并相应地命名这些表达式:
mutator <- function(f, ...) {
  f_expr <- enquo(f)
  exprs <- enquos(...)

  ## Same code as in my_mutate above, but with an arbitrary function
  set_names(
    map( exprs, ~quo((!!f_expr)(!!.x)) ),
    str_c( quo_name(f_expr), "_", map_chr(exprs, quo_name) )
  )
}

## Example usage
mutator( sd, mpg, cyl )
# $sd_mpg
# <quosure>
#   expr: ^^sd(^mpg)
#   env:  0x555e05260020

# $sd_cyl
# <quosure>
#   expr: ^^sd(^cyl)
#   env:  0x555e05273af8

我们现在可以使用新的mutator函数将my_mutate重新定义为一个简单的一行代码:
my_mutate2 <- function(df, ...) {
  mutate( df, !!!mutator(mean, ...), !!!mutator(sum, ...) )
}

0

看起来你已经使用了mutate_at找到了答案,但是如果你需要在其他情况下这样做,我会添加以下方法。

如果你使用以下函数,你会发现quos(...)返回一个与你的参数对应的quosures列表。

watch_quos <- function(...){
    quos_args <- quos(...)
    return(quos_args)
}  
# Returns a list of closures
watch_quos(hello, iam, several, arguments)

所以你可以轻松地将quos的结果转换为字符列表(或向量),并使用sapplylapply之一对每个引用的参数应用quo_name
quo_names <- function(...) {
   quos_args <- quos(...)
   char_args <- lapply(quos_args, quo_name)
   return(char_args)
}
# Returns a character list
quo_names(hello, iwill, be, char, arguments)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接