【问题标题】:R quo_name equivalent of quosR quo_name 相当于 quos
【发布时间】:2018-01-16 07:58:38
【问题描述】:

您好,关注Programming with 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)
  )
}

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

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

【问题讨论】:

  • 改用mutate_at 怎么样?函数名称将作为后缀而不是前缀添加,否则会执行相同的工作。
  • @aosmith 不幸的是,这会覆盖 {.vars} 中指定的列我想创建一个新列。
  • 别介意之前的评论,如果你命名 .funs 参数,它会添加一个新列。
  • 我认为您所描述的内容不存在,但除了@aosmith 的评论之外,您还可以通过引用名称的向量map,例如quos(a,b) %&gt;% purrr::map(~my_mutate(df, !!.x)).

标签: r dplyr nse rlang


【解决方案1】:

免责声明:虽然我认为@aosmith 提出的mutate_at 是最好和最简单的解决方案,但我认为看看如何使用rlang 工具解决问题可能具有指导意义,如果mutate_at 不存在。为了科学!

如 cmets 中所述,您将需要研究 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, ...) )
}

【讨论】:

    【解决方案2】:

    您似乎使用mutate_at 找到了答案,但如果您需要在其他情况下这样做,我将添加以下方式。

    如果您使用以下函数,您会看到 quos(...) 返回与您的参数相对应的引用列表。

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

    因此,您可以使用sapplylapply 之一轻松地将quos 的结果转换为将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)
    

    【讨论】:

      猜你喜欢
      • 2019-08-11
      • 2011-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-31
      • 2017-09-10
      • 2016-03-19
      相关资源
      最近更新 更多