我有一个定制的包裹 summary()
, print()
具有特定类的对象的方法。这个包还用了精彩dplyr
用于数据操作的包 - 我希望我的用户编写同时使用我的包和 dplyr 的脚本。
一个障碍,已被其他人注意到 here和 here是 dplyr 动词不保留自定义类 - 这意味着 ungroup
命令可以剥离我的自定义类的 data.frames,从而搞砸 summary
的方法调度, 等等。
Hadley 说“正确执行此操作取决于您 - 您需要为每个 dplyr 方法为您的类定义一个方法,以正确恢复所有类和属性”,我正在尝试使用 advice - 但我不知道如何正确包装 dplyr 动词。
这是一个简单的玩具示例。假设我定义了一个 cars
类,我有一个自定义 summary
为了它。
这有效
library(tidyverse)
class(mtcars) <- c('cars', class(mtcars))
summary.cars <- function(x, ...) {
#gather some summary stats
df_dim <- dim(x)
quantile_sum <- map(mtcars, quantile)
cat("A cars object with:\n")
cat(df_dim[[1]], 'rows and ', df_dim[[2]], 'columns.\n')
print(quantile_sum)
}
summary(mtcars)
问题来了small_cars <- mtcars %>% filter(cyl < 6)
summary(small_cars)
class(small_cars)
那个summary
调用 small_cars
只是给了我通用摘要,而不是我的自定义方法,因为 small_cars
不再保留 cars
dplyr 过滤后的类。我试过的
首先,我尝试在
filter
周围编写自定义方法( filter.cars
)。那没有用,因为 filter
实际上是 filter_
周围的包装器允许进行非标准评估。所以我写了一个自定义
filter_
cars
的方法对象,试图实现 @jwdink 的 advicefilter_.cars <- function(df, ...) {
old_classes <- class(df)
out <- dplyr::filter_(df, ...)
new_classes <- class(out)
class(out) <- c(new_classes, old_classes) %>% unique()
out
}
那不起作用 - 我得到一个无限递归错误:Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
Error during wrapup: evaluation nested too deeply: infinite recursion / options(expressions=)?
我想要做的就是获取传入 df 上的类,移交给 dplyr,然后返回具有与 dplyr 调用之前相同的类名的对象。 如何更改我的 filter_
包装器来完成那个? 谢谢!
最佳答案
更新:
自从我最初的回答以来,有些事情发生了变化:
dplyr::filter
保持类。然而,有些——比如 dplyr::group_by
- 仍然删除类(class),所以这个问题仍然存在。 最近由于第二个子弹遇到了一个难以解决的问题,所以只想举一个更完整的例子。假设您正在使用一个自定义类,名称为
custom_class
,并且您想添加一个 groupby 方法。假设您使用的是 roxygen:#' group_by.custom_class
#'
#' @description Preserve the class of a `custom_class` object.
#' @inheritParams dplyr::group_by
#'
#' @importFrom dplyr group_by
#'
#' @export
#' @method group_by custom_class
group_by.custom_class <- function(.data, ...) {
result <- NextMethod()
return(reclass(.data, result))
}
(有关 reclass
函数的定义,请参阅原始答案)强调:
@method group_by custom_class
添加 S3method(group_by,custom_class)
到命名空间 @importFrom dplyr group_by
添加 importFrom(dplyr,group_by)
到您的命名空间 我相信 R < 3.5 你可以只用第二个就可以逃脱,但现在你需要两者。
旧答案:
在 the thread 中提供了进一步的建议所以我想我会更新似乎是最佳实践,即使用
NextMethod()
.filter_.cars <- function(.data, ...) {
result <- NextMethod()
reclass(.data, result)
}
哪里reclass
是你写的;它只是一个泛型,(至少)重新添加了原始类:reclass <- function(x, result) {
UseMethod('reclass')
}
reclass.default <- function(x, result) {
class(result) <- unique(c(class(x)[[1]], class(result)))
result
}
关于r - 在 R 包中定义自定义 dplyr 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41967700/