dplyr风格的简单subset/summarise函数方法

3

背景

提供的函数实现以下功能:

  • 使用用户提供的表达式对数据框进行子集筛选
  • 选择所需列
  • 在结果向量上应用自定义汇总函数并返回标量

base方法

summarise_filtered <-
    function(df,
             subset_arg,
             summary_fun = c("min", "max", "median"),
             select_col) {
        summary_fun <- match.arg(summary_fun)

        sbst_vals <-
            subset.data.frame(
                df,
                subset = eval(parse(text = subset_arg)),
                drop = TRUE,
                select = eval(parse(text = select_col))
            )

        do.call(match.fun(summary_fun), list(sbst_vals))

    }

结果

summarise_filtered(mtcars, "am == 1", "min", "cyl")
# [1] 4
summarise_filtered(mtcars, "am == 1", "max", "cyl")
# [1] 8

挑战

我对使用dplyr管道语法重新编写上述函数感兴趣。我的初始尝试符合基本要求:

summarise_filtered_dplyrish <-
    function(df,
             subset_arg,
             summary_fun,
             select_col) {

        df %>%
            filter({{subset_arg}}) %>%
            summarise(across(.cols = {{select_col}}, .fns = summary_fun)) %>%
            pull({{select_col}})

    }

被调用时:

summarise_filtered_dplyrish(mtcars, am == 1, min, cyl)
# [1] 4

问题

我希望这个函数能够使用:

summarise_filtered_dplyrish(mtcars, "am == 1", "min", "cyl")

语法,除了已经工作的解决方案之外,还需要一种方法来实现。如何做到这一点?截至目前,上述调用会生成错误:

错误

错误:使用 filter() 输入 ..1 出现问题。x 输入 ..1 必须是逻辑向量,而不是字符。 ℹ 输入 ..1"am == 1"。运行 rlang::last_error() 以查看错误发生的位置。

1个回答

2

mincyl可以通过ensym()轻松处理,它适用于字符串和符号。表达式am == 1需要更多的工作。让我们定义一个帮助函数,只有在对象是字符串时才解析它:

str2expr <- function(.x) {if( is.character(.x) ) rlang::parse_expr(.x) else .x}

现在我们可以捕获传递给 subset_arg 的参数,并且如果它是字符串,则对其进行解析:

summarise_filtered_dplyrish <-
    function(df,
             subset_arg,
             summary_fun,
             select_col) {

        subset_expr <- rlang::enexpr(subset_arg) %>% str2expr()

        df %>%
            filter( !!subset_expr ) %>%
            summarise(across(.cols = {{select_col}}, .fns = !!ensym(summary_fun))) %>%
            pull( !!ensym(select_col) )
    }

summarise_filtered_dplyrish( mtcars, am == 1, min, cyl )        # Works
summarise_filtered_dplyrish( mtcars, "am == 1", "min", "cyl" )  # Also works

简要说明{{x}}!!enquo(x) 的缩写形式,它捕获了提供给函数参数的表达式和该表达式应该被评估的上下文。由于您的上下文实际上是由 df 定义的,因此可以将 enquo 放松到 enexpr(捕获表达式但不是评估上下文)和 ensym(捕获符号或包含符号名称的字符串)。


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