我正在尝试将 S4 对象(
dgCMatrix
包中的 Matrix
类稀疏矩阵)传递给另一个包中的函数(Wrench
)。我正在努力理解一些非常奇怪的行为。我怀疑它与 R 中的 S3/S4 方法调度有关,这是一个我不太了解的主题。
示例设置:
library(Matrix)
library(Wrench)
M <- Matrix(10 + 1:28, 4, 7)
M[, c(2,4:6)] <- 0
sM <- as(M, "sparseMatrix")
print(sM)
# 4 x 7 sparse Matrix of class "dgCMatrix"
#
# [1,] 11 . 19 . . . 35
# [2,] 12 . 20 . . . 36
# [3,] 13 . 21 . . . 37
# [4,] 14 . 22 . . . 38
print(rowSums(sM))
# [1] 65 68 71 74
接下来,我调用函数,
Wrench::wrench
来做我的分析:
wrench(sM, colData=c('A','B','A','B','B','A','B'))
# Error in h(simpleError(msg, call)) :
# error in evaluating the argument 'i' in selecting a method for function '[': 'x' must be an array of at least two dimensions
奇怪...我在RStudio的那一行打了一个断点,进入
wrench
,开始是这样的:
function (mat, condition, etype = "w.marg.mean", ebcf = TRUE,
z.adj = FALSE, phi.adj = TRUE, detrend = FALSE, ...)
{
mat <- mat[rowSums(mat) > 0, ]
# ...
进入第一行,进入对
rowSums(mat)
的呼叫,然后发现自己在base::rowSums
中。
function (x, na.rm = FALSE, dims = 1L)
{
if (is.data.frame(x))
x <- as.matrix(x)
if (!is.array(x) || length(dn <- dim(x)) < 2L)
stop("'x' must be an array of at least two dimensions")
is.data.frame(x)
是 FALSE
,is.array(x)
是 FALSE,stop()
函数被调用并且错误源自那里。好的,所以 base::rowSums
不知道如何处理稀疏矩阵并引发错误...
但是为什么在
base::rowSums
内调用rowSums(mat)
时调用wrench
,而在外部范围调用Matrix::rowSums.dgCMatrix
时调用rowSums(sM)
并且一切正常?
更一般地说,我该如何调试这种问题,方法调度没有按预期发生(尤其是在我无法控制的包中)?
我尝试过的其他事情:
debugcall
,即来自外部作用域的 eval(debugcall(rowSums(sM)))
,它把我带到 Matrix
的某个地方,它调用了一些 C 代码,最终打印了行总和。当我在 Wrench::wrench
内暂停的调试器尝试同样的事情时,我得到一个错误:
Browse[1]> eval(debugcall(rowSums(mat)))
Error in .signatureFromCall(func, mcall, env) :
trying to get slot "signature" from an object of a basic class ("function") with no slots
Advanced R的S4章节看了好几遍
你的
NAMESPACE
需要
import(Matrix)
或
importFrom("Matrix", ...)
importClassesFrom("Matrix", dgCMatrix, ...)
importMethodsFrom("Matrix", "[", rowSums, ...)
后者仅在必要时导入(鼓励您遵循的做法)。
关于调试,请注意几个附件将函数的评估环境
wrench
与其调用环境(在本例中为全局环境)分开:
1. the evaluation environment of 'wrench'
2. the 'Wrench' namespace
3. the 'Wrench' imports
4. the 'base' namespace
5. the calling environment of 'wrench'
您可以通过在调试器内部递归调用
parent.env
来验证这一点,如下所示:
> library(Wrench)
> debugonce(wrench)
> wrench()
Browse[2]> e <- environment()
Browse[2]> repeat { print(e); if (identical(e, parent.frame())) break; e <- parent.env(e) }
debug at #1: print(e)
Browse[3]> c
<environment: 0x11e032028>
<environment: namespace:Wrench>
<environment: 0x11e84bf40>
attr(,"name")
[1] "imports:Wrench"
<environment: namespace:base>
<environment: R_GlobalEnv>
Browse[2]>
当您从
Matrix导入
rowSums
的方法时,S4 通用 rowSums
将放置在您的包导入中。在 base命名空间中,通用
rowSums
在非通用 rowSums
之前找到,因为首先搜索您的包导入。调用时,泛型 rowSums
从相应的方法表中调度适当的方法。如果找不到,则调用默认方法,对于 rowSums
来说,它只是非泛型函数。
Browse[2]> rowSums
standardGeneric for "rowSums" defined from package "base"
function (x, na.rm = FALSE, dims = 1, ...)
standardGeneric("rowSums")
<bytecode: 0x1182be180>
<environment: 0x1182b78f8>
Methods may be defined for arguments: x
Use showMethods(rowSums) for currently available ones.
Browse[2]> environment(rowSums)$.MTable$CsparseMatrix
Method Definition:
function (x, na.rm = FALSE, dims = 1L, ...)
{
.local <- function (x, na.rm = FALSE, dims = 1L, sparseResult = FALSE)
.Call("CRsparse_rowSums", x, na.rm, FALSE, sparseResult)
.local(x, na.rm, dims, ...)
}
<bytecode: 0x11b88dd60>
<environment: namespace:Matrix>
Signatures:
x
target "CsparseMatrix"
defined "CsparseMatrix"
Browse[2]> rowSums@default
Method Definition (Class "derivedDefaultMethod"):
function (x, na.rm = FALSE, dims = 1, ...)
base::rowSums(x, na.rm = na.rm, dims = dims, ...)
<environment: 0x118261ff8>
Signatures:
x
target "ANY"
defined "ANY"
重要的是,如果 Wrench 从 Matrix 导入,则加载 Wrench 会自动加载 Matrix,因此由
Matrix导出的
rowSums
的方法始终在方法表中可用,以供 Wrench 中的函数使用.
这个例子的一个微妙方面是
[
运算符,不像rowSums
,是内部S4泛型。当您为 [
导入方法时,S4 泛型 [
not放置在您的包导入中,但 S4 调度仍然有效。
因此,理论上,您无需从Matrix
导入
[
的方法就可以逃脱,只要您从Matrix导入something以确保在加载Wrench时加载其方法。但这实际上只是一个技术细节;为了清楚起见,do 使用 NAMESPACE
导入您的包可能需要的所有方法。