Tre*_*vor 11 r matrix r-s3 r-s4
我想%*%为基本矩阵子类编写一个方法。我的子类是一个 S3 类,并且 的文档help("%*%")说它是一个 S4 泛型,并且需要为名为和%*%的两个参数的函数编写 S4 方法。在使用之前,我已经为 S4 泛型方法编写了 S3 类的方法,并且我查看了包的源代码以获取灵感,但由于某种原因,我无法完全让它在这种情况下工作。 显示我的方法存在于我的目标签名中,但当我尝试使用它时,我的方法似乎从未真正被 R 调用。xymethods::setOldClass()methods::setMethod(){Matrix}methods::showMethods()
x <- diag(3)
class(x) <- c("atm2d", class(matrix()))
print(x)
Run Code Online (Sandbox Code Playgroud)
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 1 0
[3,] 0 0 1
attr(,"class")
[1] "atm2d" "matrix" "array"
Run Code Online (Sandbox Code Playgroud)
默认情况%*%下会删除我的类属性,我想保留它。
print(x %*% x)
Run Code Online (Sandbox Code Playgroud)
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 1 0
[3,] 0 0 1
Run Code Online (Sandbox Code Playgroud)
我尝试%*%为我的类创建一个方法,该方法不会删除我的类属性:
as.matrix.atm2d <- function(x, ...) {
class(x) <- NULL
x
}
matmult <- function(x, y) {
v <- as.matrix(x) %*% as.matrix(y)
class(v) <- c("atm2d", class(matrix()))
v
}
methods::setOldClass("atm2d")
# this alternate `setOldClass()` also doesn't work
# methods::setOldClass(c("atm2d", class(matrix())))
methods::setMethod("%*%",
c(x = "atm2d", y = "atm2d"),
function(x, y) matmult(x, y))
Run Code Online (Sandbox Code Playgroud)
showMethods()似乎显示创建了具有预期签名的 S4 方法:
showMethods("%*%", class = "atm2d")
Run Code Online (Sandbox Code Playgroud)
Function: %*% (package base)
x="atm2d", y="atm2d"
Run Code Online (Sandbox Code Playgroud)
然而,这个方法实际上似乎并没有被调用%*%:
print(x %*% x)
Run Code Online (Sandbox Code Playgroud)
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 1 0
[3,] 0 0 1
Run Code Online (Sandbox Code Playgroud)
如果我的方法被调用,我会期望它也打印出它的类:
print(matmult(x, x))
Run Code Online (Sandbox Code Playgroud)
[,1] [,2] [,3]
[1,] 1 0 0
[2,] 0 1 0
[3,] 0 0 1
attr(,"class")
[1] "atm2d" "matrix" "array"
Run Code Online (Sandbox Code Playgroud)
Mik*_*gan 14
该%*%运算符是内部通用的,这意味着分派发生在 C 代码中。目前(即在 R 4.2.3 中),相应的 C 函数(在此处do_matprod定义)包含此检查:
if (PRIMVAL(op) == 0 && /* %*% is primitive, the others are .Internal() */
(IS_S4_OBJECT(x) || IS_S4_OBJECT(y))
&& R_has_methods(op)) {
SEXP s, value;
/* Remove argument names to ensure positional matching */
for(s = args; s != R_NilValue; s = CDR(s)) SET_TAG(s, R_NilValue);
value = R_possible_dispatch(call, op, args, rho, FALSE);
if (value) return value;
}
Run Code Online (Sandbox Code Playgroud)
如果既不是 S4 对象,x也不是 S4 对象(如您的示例所示),则继续将它们作为传统矩阵处理,而不查看任一参数的属性。您参考的部分:ydo_matprodclasshelp("%*%")
该运算符是 S4 通用的,但不是 S3 通用的。需要为名为
x和的两个参数的函数编写 S4 方法y。
试图表达这一点,但并不是特别清楚。(毕竟,您确实定义了 S4 方法。)
这里有两个主要问题:
setOldClass允许您在签名中使用 S3 类定义 S4 方法,但内部通用函数仅在参数之一是 S4 对象时查找 S4 方法(为了速度)。
%*%不是 S3 通用的,因此即使您注册了像 之类的 S3 方法%*%.zzz,它也不会被调度。
话虽如此,该%*%运算符将从 R 4.3.0 开始成为 S3 通用运算符,该运算符将于 4 月 21 日发布。您可以在新闻中找到此条目:
矩阵乘法运算符
%*%现在是 S3 泛型,属于新组 genericmatrixOps。来自 Tomasz Kalinowski 在PR#18483中的贡献。
当发生这种情况时,它将像组的其他内部通用成员%*%一样表现,因为 S3 方法将在适当的情况下调度。但是,当两个参数都不是 S4 对象时,S4 方法仍然不会被调度。+Ops%*%.zzz
可以说,setMethod当您尝试定义永远不会被调度的 S4 方法时,应该更改为发出警告或错误信号,如示例中所示。
枚举泛型函数的类型和它们调度的方法的类型可能会有所帮助,从而限制对 S3 和 S4 的关注。我们将使用此脚本来定义测试对象,每个测试对象都应在新的 R 进程中运行:
## objects.R
w <- structure(0, class = "a")
x <- structure(0, class = "b")
setOldClass("c")
y <- structure(0, class = "c")
setClass("d", contains = "numeric")
z <- new("d", 0)
Run Code Online (Sandbox Code Playgroud)
这些通过 为 S3 类和 S4 类调度 S3 方法UseMethod。当没有找到方法时,它们会调度默认方法*.default或(如果没有找到)抛出错误。他们从不调度 S4 方法。
source("objects.R")
h <- .__h__. <- function(x) UseMethod("h")
.S3method("h", "default", function(x) "default")
.S3method("h", "a", function(x) "a3")
setMethod("h", "c", function(x) "c4")
setMethod("h", "d", function(x) "d4")
h <- .__h__. # needed to undo side effect of 'setMethod'
h
## function(x) UseMethod("h")
h(w)
## [1] "a3"
h(x)
## [1] "default"
h(y)
## [1] "default"
h(z)
## [1] "default"
Run Code Online (Sandbox Code Playgroud)
这些为 S3 类(正式定义为setOldClass)和 S4 类通过调度 S4 方法standardGeneric。当没有找到方法时,他们调度默认方法*@default。如果默认方法是 S3 通用方法,则再次分派,这次分派到任何可用的 S3 方法。然而,通常默认方法只是调用stop来发出错误信号。
source("objects.R")
h <- function(x) UseMethod("h")
.S3method("h", "default", function(x) "default")
.S3method("h", "c", function(x) "c3")
setMethod("h", "d", function(x) "d4")
h
## standardGeneric for "h" defined from package ".GlobalEnv"
##
## function (x)
## standardGeneric("h")
## <environment: 0x1044650b0>
## Methods may be defined for arguments: x
## Use showMethods(h) for currently available ones.
h@default
## Method Definition (Class "derivedDefaultMethod"):
##
## function (x)
## UseMethod("h")
##
## Signatures:
## x
## target "ANY"
## defined "ANY"
h(w)
## [1] "default"
h(x)
## [1] "default"
h(y)
## [1] "c3"
h(z)
## [1] "d4"
Run Code Online (Sandbox Code Playgroud)
这些都在base中定义为原始函数。您可以参考帮助页面或源代码来确定它们是仅 S3 通用、仅 S4 通用还是 S3 和 S4 通用。在第三种情况下,仅当找不到合适的 S4 方法时才会发生 S3 调度。而且,正如我已经解释过的,仅当签名中的参数之一是 S4 对象时,S4 调度才会发生。
我们以+和%*%为例。两者都是 S4 通用,但只有+S3 通用。
source("objects.R")
.S3method("+", "default", function(e1, e2) "default")
.S3method("+", "a", function(e1, e2) "a3")
.S3method("+", "b", function(e1, e2) "b3")
.S3method("+", "c", function(e1, e2) "c3")
.S3method("+", "d", function(e1, e2) "d3")
setMethod("+", c("c", "c"), function(e1, e2) "c4")
setMethod("+", c("d", "d"), function(e1, e2) "d4")
w + w
## [1] "a3"
x + x
## [1] "b3"
y + y
## [1] "c3"
z + z
## [1] "d4"
Run Code Online (Sandbox Code Playgroud)
这里,调度 S3 方法。前三个结果是通过S3调度获得的。最后的结果是通过S4调度获得的。
source("objects.R")
.S3method("%*%", "default", function(x, y) "default")
.S3method("%*%", "a", function(x, y) "a3")
.S3method("%*%", "b", function(x, y) "b3")
.S3method("%*%", "c", function(x, y) "c3")
.S3method("%*%", "d", function(x, y) "d3")
setMethod("%*%", c("c", "c"), function(x, y) "c4")
setMethod("%*%", c("d", "d"), function(x, y) "d4")
w %*% w
## [,1]
## [1,] 0
x %*% x
## [,1]
## [1,] 0
y %*% y
## [,1]
## [1,] 0
z %*% z
## [1] "d4"
Run Code Online (Sandbox Code Playgroud)
这里,不调度 S3 方法。前三个结果是通过内部定义的默认方法获得的。最后的结果是通过S4调度获得的。