4

我需要根据要汇总的变量使用不同的汇总函数对数据框进行分组和汇总。这些函数可以有不同的主要和可选参数,我想编写一个可以完成所有这些的函数。

这是我设法编写的更简单的函数,只是为了展示它的逻辑。

require(tidyverse)
require(magrittr)
require(rlang)

example <- data.frame(y = as.factor(c('A','B','C','A','B')),
                      x1 = c(7, 10, NA, NA, 2),
                      x2 = c(13, 0, 0, 2, 1),
                      z = c(0, 1, 0, 1, 0))

# Summarise variables with common prefix
do_summary_prefix <- function(dataset, y, prefix, fun, ...){
    y <- enquo(y)
    prefix <- quo_name(enquo(prefix))
    fun <- match.fun(fun)
    dataset  %<>%  
       group_by(!!y) %>% 
       summarise_at(vars(starts_with(prefix)), funs(fun), ...) %>% 
       ungroup()
    return(dataset)
}
do_summary_prefix(example, y, x, 'quantile', probs = 0.25, na.rm = T) 

# Summarise variables with different names, one at a time
do_summary_x <- function(dataset, y, x, fun, ...){
    y <- enquo(y)
    x <- enquo(x)

    dataset  %<>%  
       group_by(!!y) %>% 
       summarise(!!paste(quo_name(x), fun, sep = '_') := do.call(match.fun(fun), list(x = !!x, ...))) %>% 
       ungroup()
    return(dataset)
}
do_summary_x(example, y, x1, fun = 'mean', na.rm = F)

这对我来说没问题,我可以do_summary_x在我想要总结的变量上使用某种循环来完成工作。但我想将循环集成到更高级别的函数中,利用...while 仍然能够为我的汇总函数使用不同的参数。

我知道我不能...用于不同的子级函数,所以我将前一个(我的变量或函数参数)之一作为列表传递,并使用do.call. 对我来说,保留...输入变量和添加参数更自然,总是用列表命名。这就是我来的:

#install.packages('plyr') # if needed
join_all <- plyr::join_all

do_summary <- function(dataset, y, ..., fun, other_args = list(NULL = 
    NULL)){
    y_quo <- enquo(y)
    y_name <- quo_name(y_quo)

    values <- quos(...)

    datasets <- lapply(values, function(value){
      summarised_data <- dataset %>% 
      group_by(!!y_quo) %>% 
      summarise(calcul = do.call(fun, 
                                 unlist(list(list(x = !!value),
                                             other_args),
                                        recursive = F))) %>%
      ungroup() %>%
      rename(!!paste(quo_name(value), stat, sep = '_') := calcul)
    return(summarised_data)
  })
  finished <- join_all(datasets, by = y_name, type = 'left')
  return(finished)
}
do_summary(example, y,
           x1, x2, z,
           stat = 'quantile',
           other_args = list(probs = 0.1, na.rm = T))
do_summary(example, y,
           x1, x2, z,
           fun = 'mean')

这工作正常,所以我对它总体上很满意,但这仅适用于具有x第一个参数的函数。

假设我还希望能够更改 的第一个参数的名称fun,即x此处。我该怎么办 ?

我还没有找到一个解决方案来引用然后注入类似的do.call东西changing_arg = !!x,或者合理使用list(!!changing_arg := !!x)

4

1 回答 1

2

以下是我将如何简化您的功能:

library(dplyr)
library(rlang)

do_summary <- function(dataset, y, ..., fun, other_args = list(NULL = NULL)){

  y_quo <- enquo(y)
  values <- quos(...)

  datasets <- dataset %>% 
      group_by(!!y_quo) %>% 
      summarise_at(vars(!!!values), .funs = fun, !!!other_args) %>%
      rename_at(vars(!!!values), paste, fun, sep = "_")

  return(datasets)
}

do_summary(example, y,
           x1, x2, z,
           fun = 'quantile',
           other_args = list(probs = 0.1, na.rm = T))

do_summary(example, y,
           x1, x2, z,
           fun = 'mean')

结果:

# A tibble: 3 x 4
       y x1_quantile x2_quantile z_quantile
  <fctr>       <dbl>       <dbl>      <dbl>
1      A         7.0         3.1        0.1
2      B         2.8         0.1        0.1
3      C          NA         0.0        0.0

# A tibble: 3 x 4
       y x1_mean x2_mean z_mean
  <fctr>   <dbl>   <dbl>  <dbl>
1      A      NA     7.5    0.5
2      B       6     0.5    0.5
3      C      NA     0.0    0.0

笔记:

  1. 您可以简单地使用and并通过显式拼接使用来提供to而不是使用lapply循环。valuessummarise_atrename_atvaluesvars!!!

  2. fun然后提供给 的.funs参数,同样,summarise_at您可以显式拼接other_args. !!!例如,list(probs = 0.1, na.rm = T)变成probs = 0.1, na.rm = T.

  3. 相同的想法rename_at。使用vars并显式拼接values。另一种方法是编写rename_at(vars(-y_name), ...),因为summarise_at只返回分组列和汇总列。

  4. 这种方法摆脱了, inthe结尾lapply的尴尬(因此也不需要)。do.callsummarise join_ally_name

  5. do_summary最后的电话quantile似乎是一个错字,而不是stat = "quantile",我认为你的意思是fun = "quantile"

  6. 请注意,仅当您以字符串形式提供函数名称时,此函数才有效。

于 2017-12-01T20:51:25.603 回答