【发布时间】:2018-06-06 15:04:14
【问题描述】:
背景
R 函数 rbind() 和 cbind() 的调度机制是非标准的。当参数之一是data.frame 时,我探索了编写rbind.myclass() 或cbind.myclass() 函数的一些可能性,但到目前为止我还没有一个令人满意的方法。这篇文章主要关注rbind,但同样适用于cbind。
问题
让我们创建一个rbind.myclass() 函数,它在被调用时简单地回显。
rbind.myclass <- function(...) "hello from rbind.myclass"
我们创建一个myclass类的对象,下面对rbind的所有调用
正确发送到rbind.myclass()
a <- "abc"
class(a) <- "myclass"
rbind(a, a)
rbind(a, "d")
rbind(a, 1)
rbind(a, list())
rbind(a, matrix())
但是,当其中一个参数(不必是第一个参数)时,rbind() 将改为调用base::rbind.data.frame():
rbind(a, data.frame())
这种行为有点令人惊讶,但它实际上记录在
dispatchrbind() 的部分。那里给出的建议是:
如果你想将其他对象与数据框结合起来, 可能需要先将它们强制转换为数据帧。
在实践中,此建议可能难以实施。转换为数据框可能会删除基本的类信息。此外,可能不知道该建议的用户在发出命令rbind(a, x) 后可能会遇到错误或意外结果。
方法
警告用户
第一种可能性是警告用户,当x 是数据框时,不应调用rbind(a, x)。相反,包mypackage 的用户应该显式调用隐藏函数:
mypackage:::rbind.myclass(a, x)
这可以做到,但用户必须记住在需要时进行显式调用。调用隐藏函数是最后的手段,不应该是常规策略。
拦截rbind
另外,我尝试通过拦截调度来屏蔽用户。我的第一次尝试是提供base::rbind.data.frame() 的本地定义:
rbind.data.frame <- function(...) "hello from my rbind.data.frame"
rbind(a, data.frame())
rm(rbind.data.frame)
这失败了,因为rbind() 没有被从.GlobalEnv 调用rbind.data.frame 欺骗,而是照常调用base 版本。
另一种策略是通过本地函数覆盖rbind(),这在S3 dispatching of `rbind` and `cbind` 中提出了建议。
rbind <- function (...) {
if (attr(list(...)[[1]], "class") == "myclass") return(rbind.myclass(...))
else return(base::rbind(...))
}
这非常适合发送到rbind.myclass(),因此用户现在可以为任何类型的对象x 键入rbind(a, x)。
rbind(a, data.frame())
缺点是在library(mypackage) 之后我们会收到消息The following objects are masked from ‘package:base’: rbind。
虽然技术上一切都按预期工作,但应该有比 base 函数覆盖更好的方法。
结论
以上替代方案都不令人满意。我已经阅读了有关使用 S4 调度的替代方案,但到目前为止,我还没有找到该想法的任何实现。有什么帮助或建议吗?
【问题讨论】:
-
AFAIK,比我更聪明的人,例如 Matt Dowle 和 Hadley Wickham 到目前为止还没有能够以优雅的方式解决这个问题。您可以在 data.table 或 tidyverse(可能是 dplyr?)包源中研究他们的解决方法。
-
谢谢。这很有帮助。我查看了
data.table中所做的事情。当用户发出library(data.table)包重新定义base::rbind.data.frame以调度到内部方法。它适用于data.table,但我担心在多个包中同时这样做的原则是自找麻烦。在 FAQ 2.23 中,马特说:“如果有更好的解决方案,我们很乐意改变它。” -
我担心我们碰壁了。 Hadley 将此称为“无法修复”github.com/tidyverse/dplyr/issues/606#issuecomment-56529411,并且似乎在经过大量尝试后放弃了。所以这里也没有解决方案......
-
如果你有一个实际的用例,我建议在 R-devel 邮件列表中提出这个问题。在我看来,这是一个应该在 R 中修复的设计缺陷。但不保证会得到修复。特别是如果您不提供补丁。
-
这是一个选项,但我确信 R 以它的工作方式工作是有原因的。我提出这个问题是希望找到一个替代方案。