你如何扩展rbind的data.frame子类?

时间:2018-02-16 01:14:37

标签: r generics methods rbind r-s3

我的问题是如何扩展rbind()以使用data.frame子类?我似乎无法正确扩展rbind()以使用一个非常简单的子类。以下示例演示了此问题:

子类和方法定义:

new_df2 <- function(x, ...)
{
  stopifnot(is.data.frame(x))
  structure(x, class = c("df2", "data.frame"), author = "some user")
}

rbind.df2 <- function(..., deparse.level = 1)
{
  NextMethod()
}

我意识到在这种情况下不需要扩展rbind(),但我的宏伟计划是在我的子类上使用rbind.data.frame(),然后在其结果中添加一些额外的检查/属性。

如果您拨打以下电话,则会收到错误消息:Error in NextMethod() : generic function not specified

不起作用:

t1 <- data.frame(a = 1:12, b = month.abb)
t2 <- new_df2(t1)
rbind(t2, t2)

我也尝试使用NextMethod(generic = "rbind"),但在这种情况下,您收到此错误:Error in NextMethod(generic = "rbind") : wrong value for .Method

也不起作用:

rbind.df2 <- function(..., deparse.level = 1)
{
  NextMethod(generic = "rbind")
}

rbind(t2, t2)

我在智慧结束时猜测我对子类/方法的理解极限。谢谢你的帮助。

2 个答案:

答案 0 :(得分:3)

我将在下面处理rbind()的特定情况,但首先请注意,我们可以生成其他示例,表明当第一个参数为NextMethod()时,...通常没有问题(关于赏金要求):

f <- function(..., b = 3) UseMethod("f")
f.a <- function(..., b = 3) { print("yes"); NextMethod() }
f.integer <- function(..., b = 4) sapply(list(...), "*", b)
x <- 1:10
class(x) <- c("a", class(x))
f(x)

[1] "yes"
      [,1]
 [1,]    4
 [2,]    8
 [3,]   12
 [4,]   16
 [5,]   20
 [6,]   24
 [7,]   28
 [8,]   32
 [9,]   36
[10,]   40

f(x, b = 5)

[1] "yes"
      [,1]
 [1,]    5
 [2,]   10
 [3,]   15
 [4,]   20
 [5,]   25
 [6,]   30
 [7,]   35
 [8,]   40
 [9,]   45
[10,]   50

那么rbind.df2为什么不起作用?

事实证明,rbind()cbind()不是 normal 泛型。首先,它们是内部通用的;请参阅Hadley Wickham在Advanced R上旧的S3页面上的“内部泛型”部分here,或摘录自当前Advanced R的以下摘录:

  

某些S3泛型,例如[,sum()和cbind(),不会调用UseMethod()   因为它们是用C实现的。相反,它们调用了C函数   DispatchGroup()或DispatchOrEval()。

这还不足以给我们造成麻烦,正如我们可以看到以sum()为例:

sum.a <- function(x, na.rm = FALSE) { print("yes"); NextMethod() } 
sum(x)

[1] "yes"
[1] 55

但是,对于rbindcbind来说,它甚至更奇怪,正如source code中的注释所承认的(从第1025行开始):

/* cbind(deparse.level, ...) and rbind(deparse.level, ...) : */
/* This is a special .Internal */

...(省略了一些代码)...

    /* Lazy evaluation and method dispatch based on argument types are
     * fundamentally incompatible notions.  The results here are
     * ghastly.

此后,给出了调度规则的一些解释,但是到目前为止,我还无法使用该信息来使NextMethod()工作。在上面给出的用例中,我将遵循评论中F. Privé的建议,并执行以下操作:

new_df2 <- function(x, ...)
{
    stopifnot(is.data.frame(x))
    structure(x, class = c("df2", "data.frame"))
}

rbind.df2 <- function(..., deparse.level = 1)
{
    print("yes") # Or whatever else you want/need to do
    base::rbind.data.frame(..., deparse.level = deparse.level)
}

t1 <- data.frame(a = 1:12, b = month.abb)
t2 <- new_df2(t1)
rbind(t2, t2)

[1] "yes"
    a   b
1   1 Jan
2   2 Feb
3   3 Mar
4   4 Apr
5   5 May
6   6 Jun
7   7 Jul
8   8 Aug
9   9 Sep
10 10 Oct
11 11 Nov
12 12 Dec
13  1 Jan
14  2 Feb
15  3 Mar
16  4 Apr
17  5 May
18  6 Jun
19  7 Jul
20  8 Aug
21  9 Sep
22 10 Oct
23 11 Nov
24 12 Dec

答案 1 :(得分:1)

答案是扩展rbind2,而不是rbind。在rbind2的帮助页面中:

“这些是具有默认方法的(S4)通用函数。

...

当满足以下两个条件时,cbind2(rbind2)的主要用途将由cbind()(rbind())递归调用:

  • 至少有一个参数是S4对象,并且

  • S3分发失败(请参阅cbind下的“分发”部分)。“