使 plyr::ddply 代码与 dplyr 等效自定义函数兼容

Make plyr::ddply code compatible with dplyr-equivalent custom function

我正在尝试调整一个长函数 (rcompanion::groupwiseMean) 以在其代码中使用 dplyr 而不是 plyr::ddply 以避免依赖现已弃用的 plyr 包.

我想定义一个自定义的 ddply2 函数,采用与原始 plyr 函数相同的参数,但在后台使用 dplyr。好处是只在现有 long function/script 的顶部重新定义函数一次,而不更改任何其他内容。到目前为止,我的尝试都失败了。下面演示。

我一直在使用这个资源:plyr::ddply equivalent in dplyr

原始plyr:ddplyr调用

data <- mtcars
var <- "mpg"
group <- c("cyl", "am")

# Original plyr:ddply-fed function:
fun.y <- function(x, idx) { length(x[, idx]) }

# Original plyr:ddply call:
plyr::ddply(.data = data, .variables = group, var, .fun = fun.y)
#>   cyl am V1
#> 1   4  0  3
#> 2   4  1  8
#> 3   6  0  4
#> 4   6  1  3
#> 5   8  0 12
#> 6   8  1  2

这是我无法重写的函数

fun.y <- function(x, idx) { length(x[, idx]) }

但这只是一个例子。以下是我需要使用的其他一些功能 ddply2:

fun.z <- function(x, idx) { as.numeric(mean(x[, idx], trim = trim, na.rm = na.rm)) }
fun.w <- function(x, idx) {
      mean(boot(x[, idx], function(y, j) mean(y[j], trim = trim,
                                              na.rm = na.rm), R = R, ...)$t[, 1])
}

现在让我们继续进行所需的 ddply2 调用,我可以随意修改它。但是它必须采用与 plyr::ddply.

相同的参数

尝试将 plyr:ddply 重写为 dpply2

library(dplyr)

ddply2 <- function(.data, .variables, var, .fun) {
  .data %>%
    group_by(across({{.variables}})) %>%
    do(.fun(., {{var}}))
}

ddply2(.data = data, .variables = group, var, .fun = fun.y)
# Error in `do()`:
# ! Results 1, 2, 3, 4, 5, 6 must be data frames, not integer.

编辑

同样,我无法重写 fun.yfun.zfun.w,只能重写 ddply2。因此基于 summarize()count() 的解决方案将不起作用,因为它们不能推广到其他功能。 plyr:ddplyr 不需要 summarize()count(),就是这个意思。

经过一些讨论,我现在明白了,我们需要的是使用 dplyr 而不是 plyr 重写这个函数,这样对于下面输入部分中列出的输入,它会给出相同的结果。

dd <- function(data, group, var, fun) 
  plyr::ddply(.data = data, .variables = group, var, .fun = fun)

为此,新函数可以将 group_by 与汇总或 group_modify 结合使用。下面的 dd1 使用第一个,dd2 使用第二个。使用您喜欢的任何一个。

请注意,fun.z 的编写方式假定一个数据帧而不是一个 tibble(因为数据帧 return 如果只有一列则为向量,而 tibble returns另一个 tibble) 所以我们使用 as.data.frame 来确保这一点。同样 plyr returns 是一个数据帧,在 dd1 和 dd2 的末尾,我们将生成的 tibble 转换为数据帧以确保结果相同。

dd1 <- function(data, group, var, fun)
  data %>% 
    group_by(across(all_of(group))) %>%
    summarize(V1 = fun(as.data.frame(cur_data()), var), .groups = "drop") %>%
    as.data.frame

dd2 <- function(data, group, var, fun)
  data %>%
    group_by(across(all_of(group))) %>%
    group_modify(~ { data.frame(V1 = fun(as.data.frame(.), var)) }) %>%
    ungroup %>%
    as.data.frame

现在测试一下

# inputs - start #

data <- mtcars
trim <- 0
na.rm <- FALSE
var <- "mpg"
group <- c("cyl", "am")

fun.z <- function(x, idx) { 
  as.numeric(mean(x[, idx], trim = trim, na.rm = na.rm))
}

# inputs - end #

library(dplyr)

dd.out <- dd(data, group, var, fun.z) # plyr
dd1.out <- dd1(data, group, var, fun.z)
dd2.out <- dd2(data, group, var, fun.z)

identical(dd1.out, dd.out)
## [1] TRUE

identical(dd2.out, dd.out)
## [1] TRUE