我有一个包含自定义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
个对象编写了一个自定义filter_.cars <- function(df, ...) {
old_classes <- class(df)
out <- dplyr::filter_(df, ...)
new_classes <- class(out)
class(out) <- c(new_classes, old_classes) %>% unique()
out
}
方法,试图实现@jwdink的advice
Error: evaluation nested too deeply: infinite recursion / options(expressions=)?
Error during wrapup: evaluation nested too deeply: infinite recursion / options(expressions=)?
这不起作用 - 我得到一个无限递归错误:
filter_
我想要做的就是获取传入的df上的类,移交给dplyr,然后返回具有与dplyr调用之前相同的类名的对象。 如何更改我的 $('body').on('click', '.remove', function() {
$(this).parents('.weekly').remove();
});
$('body').on('click', '.add', function() {
$("#template").before('<div class="well weekly">' + $('#template').html() + '</div>');
});
包装器以实现此目的?谢谢!
答案 0 :(得分:8)
您的新filter_
方法尝试应用于定义中的新类,因此递归。
关注the advice in the issue you linked,请尝试在更新后的方法中删除filter_
之前的新课程。
class(out) <- class(out)[-1]
答案 1 :(得分:8)
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
}
但是你可以为你的类定义一个自定义方法,它也可以复制属性:
reclass.cars <- function(x, result) {
class(result) <- unique(c(class(x)[[1]], class(result)))
attr(result,'cars') <- attr(x,'cars')
result
}
我实际上认为更好的默认方法只是假设有一个名称与类相同的属性:
reclass.default <- function(x, result) {
class(result) <- unique(c(class(x)[[1]], class(result)))
attr(result, class(x)[[1]]) <- attr(x, class(x)[[1]])
result
}
请注意,对于dplyr 0.7,不推荐使用动词的下划线版本。如果你的汽车&#39; class继承自tbl_df
,你需要为非下划线动词编写一个方法。但是,您可能希望保留下划线版本以实现向后兼容性。
鉴于所有这些复制,我有点像这里的副词的想法。
preservatively <- function(fun) {
function(x, ...) {
result <- NextMethod()
reclass(x, result)
}
}
然后你的包装里的东西很简洁:
filter_.cars <- preservatively(filter_)
filter.cars <- preservatively(filter)
mutate_.cars <- preservatively(mutate_)
mutate.cars <- preservatively(mutate)
等
编辑:
不要使用preservatively
。如果有人使用命名的第一个参数调用dplyr动词,它将会中断,因为该名称通常是.data
,而不是x
。
filter.cars <- preservatively(filter)
filter(my_data, condition) # good
filter(.data = my_data, condition) # oh no
如果事实证明副词可以正常工作,我会更新此答案。否则,我想这真的不再冗长:
filter.cars <- function(.data, ...) reclass(.data, NextMethod())