将带有参数的函数传递给 r 中的函数参数

Pass in function with parameters to function arguement in r

我很难理解将某些东西传递给 R 中的函数时的所有细微差别。rlang 包及其使用方法让我感到困惑。无法找到有关何时使用所有各种 rlang::sym 或相关功能的良好指南。

无论如何,我正在尝试制作一个允许传入用户定义函数和相关参数的函数。例如均值、分位数等。我希望 user_metric 始终用引号引起来,并且它还需要能够包含各种参数本身,例如 na.rm = TRUE 等等。有人可以告诉我这是如何工作的,所以我想传入 'mean' 还是 'mean(. , na.rm=TRUE)' 都可以?

library(tidyverse)
group_by_metrics=function(data, group_col, user_metric){

metrics = data %>% group_by(!!rlang:sym(group_col)) %>% summarise_all(.funs = funs(!!rlang::syms(user_metric))

return(metrics)
}

group_by_metrics(data=mtcars, group_col='vs', user_metric='mean')
group_by_metrics(data=mtcars, group_col='vs', user_metric='mean(., na.rm = TRUE)'
group_by_metrics(data=mtcars, group_col='vs', user_metric ='quantile(., probs=0.95, na.rm = TRUE')

您必须区分第一种情况(您只需提供函数名称)和其他情况(您有效定义 lambda 函数)。对于前者,您可以使用 match.fun 按名称查找函数。对于后者,将您的字符串转换为公式,然后使用 purrr::as_mapper() 从中创建函数。使用 ensym 而不是 sym 以允许不带引号的参数。

group_by_metrics <- function(.data, group_col, user_metric)
{
  f <- purrr::possibly( match.fun, NULL )(user_metric)
  if( is.null(f) )
      f <- str_c( "~", user_metric ) %>% as.formula %>% as_mapper
  .data %>% group_by(!!rlang::ensym(group_col)) %>% summarize_all( f )
}

group_by_metrics( mtcars, "vs", "quantile(., probs=0.95, na.rm=TRUE)" )
#      vs   mpg   cyl  disp    hp  drat    wt  qsec    am  gear  carb
#   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1     0  21.7     8  462.  275.  4.25  5.36  18.0     1  5     6.30
# 2     1  32.9     6  237.  123   4.47  3.45  21.2     1  4.35  4   

## Using ensym instead of sym allows you to drop " for group_col
group_by_metrics( mtcars, vs, "mean" )
#      vs   mpg   cyl  disp    hp  drat    wt  qsec    am  gear  carb
#   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1     0  16.6  7.44  307. 190.   3.39  3.69  16.7 0.333  3.56  3.61
# 2     1  24.6  4.57  132.  91.4  3.86  2.61  19.3 0.5    3.86  1.79

请注意,如果您使用 ...:

单独传递附加参数,则可以避免所有这些转换
group_by_metrics2 <- function(.data, group_col, user_metrics, ...)
{ 
  .data %>% group_by(!!rlang::ensym(group_col)) %>% 
    summarize_all( user_metrics, ... ) 
}

group_by_metrics2( mtcars, "vs", "quantile", probs=0.05, na.rm=TRUE)
# # A tibble: 2 x 11
#      vs   mpg   cyl  disp    hp  drat    wt  qsec    am  gear  carb
#   <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl> <dbl>
# 1     0  10.4   5.7 141.  107.   2.90  2.55  14.6     0     3     2
# 2     1  18.0   4    74.1  58.5  2.97  1.58  17.8     0     3     1

在最后一个示例中,字符串引号 "vsquantile 两边都是可选的。