编写自定义包装器时如何允许 dplyr::summarise() 中的可选摘要计算

How to allow optional summary computations in dplyr::summarise() when writing a custom wrapper

编写自定义包装函数时,enable/disable 在 dplyr::summarise() 内进行额外计算的简洁方法是什么?

例如,考虑以下接收数据并允许用户获取数据中特定列的均值和标准差的函数:

library(dplyr)
library(tidyr)

get_means <- function(data, var_to_average) {
  
  data %>%
    pivot_longer(cols = {{ var_to_average }}, values_to = "response") %>%
    group_by(name) %>%
    summarise(mean = mean(response, na.rm = TRUE),
              sd = sd(response, na.rm = TRUE), .groups = "drop")
}

get_means(mtcars, mpg)

# A tibble: 1 x 3
  name   mean    sd
* <chr> <dbl> <dbl>
1 mpg    20.1  6.03

但是如果我想让 sd 的计算成为可选的呢?

一个选择是做一个非常重复的代码:

get_means_repetitive <- function(data, var_to_average, get_sd = NULL) {
  
  if (is.null(get_sd)) {
    data %>%
      pivot_longer(cols = {{ var_to_average }}, values_to = "response") %>%
      group_by(name) %>%
      summarise(mean = mean(response, na.rm = TRUE),
                .groups = "drop") 
    
  } else if (get_sd) {
    
    data %>%
      pivot_longer(cols = {{ var_to_average }}, values_to = "response") %>%
      group_by(name) %>%
      summarise(mean = mean(response, na.rm = TRUE),
                sd = sd(response, na.rm = TRUE), .groups = "drop")
  }

}

我想避免这样的代码有几个原因。首先,它是重复的并且容易出错。其次,理想情况下,我想让函数的其他部分“可调整”(即可以切换 on/off),因此我需要一种简单的方法来允许组件组合 on/off。依赖 if-else 块是非常有限的。


有没有更简洁的方法来实现这一点?

只是一个想法,但我提出的方式行不通(我什至不确定这是正确的方向)

get_means_succinct <- function(data, var_to_average, get_sd = NULL) {
  
  if (is.null(get_sd)) {
    include_sd <- NULL
  } else {
    include_sd <- sd(response, na.rm = TRUE)
  }
  
  data %>%
    pivot_longer(cols = {{ var_to_average }}, values_to = "response") %>%
    group_by(name) %>%
    summarise(mean = mean(response, na.rm = TRUE),
              sd = include_sd, .groups = "drop")
}

有什么想法吗?


编辑


基于@G。 Grothendieck 的回答我想强调一下,我的问题使用 sd() 作为示例。我正在寻找一种在代码可读性和代码速度方面都高效的通用编码解决方案。我想避免 evaluation/calculation 可选参数,除非他们被要求(在这个例子中是是否计算 sd)。

如果 mean 和 sd 仅用于示例目的并且实际上代表了一个长计算,请使用 if 来阻止它们的计算,然后 select 在最后一行中输出所需的列。

(如果它真的只是 mean 和 sd 它们的计算速度如此之快以至于避免它们的计算可能没有意义,在这种情况下我们可以省略 if's 并只使用 select 在结束以提取所需的计算它们,即使我们不使用它们也是如此。)

get_means2 <- function(data, var_to_average, stats = c("mean", "sd")) {   
  data %>%
    pivot_longer(cols = {{ var_to_average }}) %>%
    group_by(name) %>%
    summarise(
      mean = if ("mean" %in% stats) mean(value, na.rm = TRUE) else NA,
      sd = if ("sd" %in% stats) sd(value, na.rm = TRUE) else NA, .groups = "drop") %>%
    select(name, stats)
}

get_means2(mtcars, mpg) # mean, sd
get_means2(mtcars, mpg, "mean") # only mean
get_means2(mtcars, mpg, "sd") # only sd