6

我想为 data.frame 与新 S3 类的对象相乘定义我自己的行为(方法)。但我不知道如何让方法调度来找到我的方法。有办法吗?

首先,我定义了 S3 对象 'a' (oldClass "A") 和 'df' (oldClass "data.frame"):

a <- 4
oldClass(a) <- "A"
df <- data.frame(x=1:2,y=3:4)

然后我使用 trace(Ops.data.frame, edit=TRUE) 在第一行添加 print("Ops.data.frame") 。这样,我知道何时调用 Ops.data.frame。这是一个演示:

a*df
# [1] "Ops.data.frame"
# x  y
# 1 4 12
# 2 8 16

我可以为“A”类定义一个 S3 方法。

Ops.A <- function(e1, e2) {
  print("Ops.A")
  oldClass(e1) <- oldClass(e1)[oldClass(e1) != "A"]
  oldClass(e2) <- oldClass(e2)[oldClass(e2) != "A"]
  callGeneric(e1, e2)
}

这被称为a 但不是df:

# This successfully calls Ops.A
a*a
# [1] "Ops.A"
# [1] 16

# But this throws an error
a*df
# Error in a * df : non-numeric argument to binary operator
# In addition: Warning message:
#   Incompatible methods ("Ops.A", "Ops.data.frame") for "*" 

所以这行不通。

remove(Ops.A)

那么使用 S4 方法呢?这需要定义 S4 类“A”,但通常 S4 调度仍会找到具有 oldClass“A”的 S3 对象。

setClass("A", list("numeric")) # Required to define a method for "A"
setGeneric("ATypicalMethod", function(e1, e2) {print("ATypicalMethod - default")})
setMethod("ATypicalMethod", signature=c("A","A"), function(e1, e2) {print("ATypicalMethod - A,A")})
ATypicalMethod(a,a)
# [1] "ATypicalMethod - A,A"

但是,这对 Ops 不起作用。

setMethod("Ops", signature=c("A","data.frame"), function(e1, e2) {
  print("Ops(A,data.frame)")
  callGeneric(e1@.Data, e2)
})
# Nope - when the scalar is an S3 object, we never find Ops(A,data.frame):
a*df
# [1] "Ops.data.frame"
# x  y
# 1 4 12
# 2 8 16

Martin Morgan ( https://stackoverflow.com/a/12101238/3203184 ) 和 ?Methods 解释了 df 的这种行为,他们说如果直接调用 S3 泛型,则永远找不到 S4 方法;这似乎发生在df 中,因为 a 和 df 都是 S3 对象。

而且打电话也无济于事setOldClass;问题不在于 S3 对象无法被 S4 方法分派识别而是当两个 S3 对象传递给*. 在这些情况下,直接调用 S3 泛型,没有多少 S4 标签会导致 S4 分派。

setOldClass("A", S4Class="A")
a*df
# [1] "Ops.data.frame"
# x  y
# 1 4 12
# 2 8 16

所以现在我很茫然。我没有办法*为我的 S3 对象找到 S4 方法,也没有办法编写取代 data.frame 方法的 S3 方法。

如果我愿意将标量设为 S4 对象,我可以获得我想要的调度:

a <- new("A", 4)
a*df
# [1] "Ops(A,data.frame)"
# [1] "Ops.data.frame"
# x  y
# 1 4 12
# 2 8 16

但我真的希望将“a”保留为 S3 对象。有没有办法(1)让'a'成为S3和(2)为Ops('A','data.frame')定义我自己的方法?

4

1 回答 1

0

非常丑陋的方式:覆盖'*'函数

a <- 4
oldClass(a) <- "A"
df <- data.frame(x=1:2,y=3:4)

my_add_df <- function(e1, e2) {
  print('my_add_df')
  print(e1)
  print(e2)
}

`*` <- function(e1, e2) {
  if (inherits(e1, 'A') && inherits(e2, 'data.frame'))
    my_add_df(e1, e2)
  else 
    .Primitive("*")(e1, e2)
}

a <- 4
oldClass(a) <- "A"
df <- data.frame(x=1:2,y=3:4)

my_add_df <- function(e1, e2) {
  print('my_add_df')
  print(e1)
  print(e2)
}

`*` <- function(e1, e2) {
  if (inherits(e1, 'A') && inherits(e2, 'data.frame'))
    my_add_df(e1, e2)
  else 
    .Primitive("*")(e1, e2)
}
于 2014-04-08T15:24:27.753 回答