为`data.frame`发送`rbind`和`cbind`

Ste*_*ren 17 r dispatch rbind cbind

背景

的调度机制R的功能rbind()cbind()是非标准.当其中一个论点是a时,我探索了写作rbind.myclass()cbind.myclass()函数的一些可能性data.frame,但到目前为止我还没有一个令人满意的方法.这篇文章集中于rbind,但同样适用cbind.

问题

让我们创建一个rbind.myclass()函数,只需在调用时回显.

rbind.myclass <- function(...) "hello from rbind.myclass"
Run Code Online (Sandbox Code Playgroud)

我们创建了一个类的对象,myclass以下调用rbind所有正确的调度rbind.myclass()

a <- "abc"
class(a) <- "myclass"
rbind(a, a)
rbind(a, "d")
rbind(a, 1)
rbind(a, list())
rbind(a, matrix())
Run Code Online (Sandbox Code Playgroud)

但是,当其中一个参数(这不一定是第一个)时,rbind()将调用base::rbind.data.frame():

rbind(a, data.frame())
Run Code Online (Sandbox Code Playgroud)

这种行为有点令人惊讶,但它实际上已在文档中 dispatch记录rbind().给出的建议是:

如果要将其他对象与数据帧组合在一起,可能需要先将它们强制转换为数据帧.

实际上,这个建议可能难以实施.转换为数据框可能会删除基本类信息.此外,在发出命令之后,可能不知道该建议的用户可能会遇到错误或意外结果rbind(a, x).

途径

警告用户

第一种可能性是警告用户在数据帧rbind(a, x)时不应该进行呼叫x.相反,包的用户mypackage应该显式调用隐藏函数:

mypackage:::rbind.myclass(a, x)
Run Code Online (Sandbox Code Playgroud)

这可以完成,但用户必须记住在需要时进行显式调用.调用隐藏函数是最后的手段,不应该是常规策略.

截距 rbind

或者,我试图通过拦截派遣来保护用户.我的第一个尝试是提供一个本地定义base::rbind.data.frame():

rbind.data.frame <- function(...) "hello from my rbind.data.frame"
rbind(a, data.frame())
rm(rbind.data.frame)
Run Code Online (Sandbox Code Playgroud)

这失败了,因为rbind()没有被愚弄rbind.data.frame.GlobalEnv,并base像往常一样调用版本.

另一种策略是rbind()通过本地函数覆盖,这在S3调度`rbind`和`cbind`时提出.

rbind <- function (...) {
  if (attr(list(...)[[1]], "class") == "myclass") return(rbind.myclass(...))
  else return(base::rbind(...))
}
Run Code Online (Sandbox Code Playgroud)

这适用于调度rbind.myclass(),因此用户现在可以键入rbind(a, x)任何类型的对象x.

rbind(a, data.frame())
Run Code Online (Sandbox Code Playgroud)

缺点是library(mypackage)我们收到消息后The following objects are masked from ‘package:base’: rbind.

虽然从技术上讲,一切都按预期工作,但应该有比base功能覆盖更好的方法.

结论

上述替代方案均不令人满意.我已经阅读了有关使用S4调度的替代方案,但到目前为止我还没有找到任何实现这个想法.任何帮助或指针?

Mar*_*ler 5

正如你自己提到的那样,使用S4将是一个很好的解决方案.我最近没有调查数据帧,因为我对其他广义矩阵更感兴趣,我的长期CRAN包'Matrix'(="推荐",即每个R分布的一部分)和'Rmpfr'.

实际上甚至有两种不同的方式:
1)Rmpfr使用新方法在rbind()/ cbind()中定义'...'的方法.这在?dotsMethods(助记符:'...'=点)中有详细记载,并在Rmpfr/R/array.R第511行中实现(例如https://r-forge.r-project.org/scm/viewvc.php /pkg/R/array.R?view=annotate&root=rmpfr)

2)Matrix通过为rbind2()和cbind2()定义(S4)方法来使用旧方法:如果你读过?rbind它确实提到了它并且当使用rbind2/cbind2时.其中的想法:"2"意味着您定义了S4方法,其中包含两个("2")矩阵类对象的签名,rbind/cbind以递归方式使用它们的两个可能的多个参数.


Pat*_*rry 1

我认为你无法想出完全令人满意的东西。您能做的最好的事情就是导出,rbind.myclass以便用户可以直接调用它,而无需执行任何操作mypackage:::rbind.myclass。如果您愿意,您可以将其称为其他名称(dplyr称为其版本bind_rows),但如果您选择这样做,我会使用一个能引起人们注意的名称rbind,例如rbind_myclass.

即使您可以让 r-core 同意更改分派行为,以便rbind分派其第一个参数,仍然存在用户希望将rbind多个对象与myclass第一个以外的对象一起使用的情况。用户还可以如何发送到rbind.myclass(df, df, myclass)

这个data.table解决方案似乎很危险;如果 CRAN 维护者进行检查并在某个时候不允许这样做,我不会感到惊讶。