调试意外的 S4 方法调度

问题描述 投票:0回答:1

我正在尝试将 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 RS4章节看了好几遍

r bioconductor r-s4
1个回答
1
投票

你的

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"

重要的是,如果 WrenchMatrix 导入,则加载 Wrench 会自动加载 Matrix,因此由

Matrix
导出的 rowSums 的方法始终在方法表中可用,以供 Wrench 中的函数使用.

这个例子的一个微妙方面是

[
运算符,不像
rowSums
,是内部S4泛型。当您为
[
导入方法时,S4 泛型 [
not
放置在您的包导入中,但 S4 调度仍然有效。

因此,理论上,您无需Matrix

导入
[的方法就可以逃脱,只要您从Matrix导入something以确保在加载Wrench时加载其方法。但这实际上只是一个技术细节;为了清楚起见,do 使用
NAMESPACE
导入您的包可能需要的所有方法。

© www.soinside.com 2019 - 2024. All rights reserved.