dplyr和非标准评估(NSE)

5

我正在尝试编写一个函数,它接受数据框的名称和要用dplyr汇总的列名,然后返回已汇总的数据框。我尝试了许多来自lazyeval包的interp()函数的排列组合,但我花费了太多时间来尝试让它工作。因此,我在这里编写了一个“静态”版本的所需函数:

summarize.df.static <- function(){
  temp_df <- mtcars %>%
    group_by(cyl) %>%
    summarize(qsec = mean(qsec),
              mpg=mean(mpg))
  return(temp_df)
}

new_df <- summarize.df.static()
head(new_df)

这是我卡住了的动态版本开头:
summarize.df.dynamic <- function(df_in,sum_metric_in){
  temp_df <- df_in %>%
    group_by(cyl) %>%
    summarize_(qsec = mean(qsec),
              sum_metric_in=mean(sum_metric_in)) # some mix of interp()
  return(temp_df)
}

new_df <- summarize.df.dynamic(mtcars,"mpg")
head(new_df)

请注意,本示例中的列名也应来自传递的参数(在此示例中为mpg)。 还要注意,qsec列是静态的,即无需传递。
以下是“docendo discimus”发布的正确答案:
summarize.df.dynamic<- function(df_in, sum_metric_in){
  temp_df <- df_in %>%
    group_by(cyl) %>%
    summarize_(qsec = ~mean(qsec), 
               xyz = interp(~mean(var), var = as.name(sum_metric_in))) 

  names(temp_df)[names(temp_df) == "xyz"] <- sum_metric_in  
  return(temp_df)
}

new_df <- summarize.df.dynamic(mtcars,"mpg")
head(new_df)

#  cyl     qsec      mpg
#1   4 19.13727 26.66364
#2   6 17.97714 19.74286
#3   8 16.77214 15.10000

new_df <- summarize.df.dynamic(mtcars,"disp")
head(new_df)

#  cyl     qsec     disp
#1   4 19.13727 105.1364
#2   6 17.97714 183.3143
#3   8 16.77214 353.1000
3个回答

7

对于具体的示例(包括静态的“qsec”等),您可以执行以下操作:

library(dplyr)
library(lazyeval)
summarize.df <- function(data, sum_metric_in){
  data <- data %>%
    group_by(cyl) %>%
    summarize_(qsec = ~mean(qsec), 
               xyz = interp(~mean(var), var = as.name(sum_metric_in))) 

  names(data)[names(data) == "xyz"] <- sum_metric_in  
  data
}

summarize.df(mtcars, "mpg")
#Source: local data frame [3 x 3]
#
#  cyl     qsec      mpg
#1   4 19.13727 26.66364
#2   6 17.97714 19.74286
#3   8 16.77214 15.10000

据我所知,目前(还)无法向dplyr::rename提供“sum_metric_in”输入,这通常用于重命名列,这就是为什么我在示例中采用了不同的方法。

谢谢@docendo-discimus!这正是我在寻找的,包括列重命名。重命名很重要,因为我有一个单独的动态函数使用ggplot2来绘制结果,轴标签来自列名。NSE非常酷,但真的让我头疼。 - Tyler Muth
不客气,@TylerMuth。我同意,需要一段时间才能习惯SE/NSE的细节。 - talat
3
可以,但需要使用.dots参数,并使用setNames()为列表命名。有点麻烦 :/ - hadley

4

您可以使用paste~来获取一个引用输入,以便summarize_能够理解。

df_in %>%
  group_by(cyl) %>%
  summarize_(qsec = ~mean(qsec),
             sum_metric_in=paste0('mean(', sum_metric_in, ')'))

3

使用 dplyr 的开发版本(即将在2017年4月发布的 0.6.0 版本) ,我们还可以利用 quosures

summarise.dfN <- function(df, expr) {
      expr <- enquo(expr) 
      colN <- quo_name(expr)
     df %>%
       group_by(cyl) %>%
       summarise(qsec = mean(qsec),
             !!colN := mean(!!expr))


  }

summarise.dfN(mtcars, mpg)
# A tibble: 3 × 3
#    cyl     qsec      mpg
#  <dbl>    <dbl>    <dbl>
#1     4 19.13727 26.66364
#2     6 17.97714 19.74286
#3     8 16.77214 15.10000
enquosubstitute类似,将输入值作为quosure返回;而quo_name将表达式转换为字符串,我们可以在group_by/summarise/mutate等中使用反引号(!!UQ)进行求值。如上所述,我们还可以将分组变量作为参数传递。
summarise.dfN2 <- function(df, expr, grpVar) {
  expr <- enquo(expr) 
  grpVar <- enquo(grpVar)
  colN <- quo_name(expr)
 df %>%
   group_by(!!grpVar) %>%
   summarise(qsec = mean(qsec),
         !!colN := mean(!!expr))


 }

summarise.dfN2(mtcars, mpg, cyl)
# A tibble: 3 × 3
#    cyl     qsec      mpg
#  <dbl>    <dbl>    <dbl>
#1     4 19.13727 26.66364
#2     6 17.97714 19.74286
#3     8 16.77214 15.10000

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