1动机

大量的Bioconductor包包含该标准的扩展SummarizedExperiment类的SummarizedExperiment包中。的功能使开发人员能够利用2021欧洲杯体育投注开户SummarizedExperiment用于同步数据和元数据的表示,同时仍然为特定的科学应用提供专门的数据结构。本文档旨在为创建这些派生类提供开发人员级别的“最佳实践”参考。

2派生一个简单的类

2.1概述

为了介绍各种概念,我们将从一个不添加任何新插槽的简单派生类开始。当需要在派生类上放置额外的约束时,这偶尔是有用的。在这个例子中,我们假设我们希望我们的类最少持有一个“计数”含有非阴性值的化验1为了简单起见,我们不担心强制执行整型,因为小数值是可能的,例如,在处理预期计数时。

2.2定义类及其构造函数

我们为新类命名CountSE并使用setClass函数从方法包,这是所有S4类的常规做法。我们用Roxygen的#”中的导入/导出语句的生成名称空间我们的包裹。

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

定义一个接受计数矩阵的构造函数来创建CountSE对象。我们使用...将进一步的参数传递给SummarizedExperiment构造函数,这允许我们避免重新指定它的所有参数。

#' @export #' @importFrom summarizeexperimental summarizeexperimental CountSE <- function(counts,…){se <- summarizeexperimental (list(counts=counts),…).CountSE(se)}

2.3定义有效性方法

我们定义了一个有效性方法,该方法执行我们前面描述的约束。这是通过使用定义一个有效性函数来实现的setValidity2S4Vectors2这允许我们关闭内部函数中的有效性检查,因为中间对象在函数范围内可能无效。.返回一个字符串表示有问题并在R会话中触发一个错误。

setValidity2("CountSE",函数(对象){msg <- NULL if (assayNames(对象)[1]!= "counts") {msg <- c(msg, "'counts'必须是第一个化验")}if (min(化验(对象))< 0){msg <- c(msg, "'counts'中的负值")}if (is.null(msg)) {TRUE} else msg})
##类“CountSE”[在]。GlobalEnv"] ## ##插槽:## ##名称:colData assays NAMES elementMetadata ##类:DataFrame Assays_OR_NULL character_OR_NULL DataFrame ## ##名称:元数据##类:列表## ##扩展:##类“summarizeexperiment”,直接##类“RectangularData”,由类“summarizeexperiment”,距离2 ##类“Vector”,由类“summarizeexperiment”,距离2 ##类“Annotated”,由类“summarizeexperiment”,距离3 ##类“vector_OR_Vector”,由类“summarizeexperiment”,距离3 ##

当提供计数时,构造函数产生预期的输出:

CountSE(矩阵(rpois(100, lambda=1), ncol=5))
##类:CountSE ## dim: 20 5 ##元数据(0):## assays(1):计数## rownames: NULL ## rowData names(0): ## colnames: NULL ## colData names(0):

…和(预期的)错误,否则:

CountSE(矩阵(rnorm (100), ncol = 5))
##错误在validObject(.Object):无效类“CountSE”对象:##负数在“counts”

2.4定义getter方法

泛型是一组具有相同名称、作用于不同类的函数。在对对象调用泛型时,S4调度系统将基于对象类选择要使用的最合适函数。这允许用户和开发人员编写与输入类类型无关的代2021欧洲杯体育投注开户码。

假设用反转符号获得计数具有特殊的科学意义。我们观察到,目前没有执行此任务的泛型,例如inBiocGenericsS4Vectors3.如果您有一个普遍适用的通用的想法,但还没有,请联系Bioconductor核心团队。.相反,我们定义了一个新的泛型negcounts

#' @export setGeneric("negcounts", function(x,…)
##[1]“negcounts”

然后定义一个特定的方法CountSE4...在泛型函数中定义意味着自定义参数像withDimnames =如有必要,可提供具体方法。

#' @export #' @importFrom summarizeexperimental化验setMethod("negcounts", "CountSE", function(x, withDimnames=TRUE){-化验(x, withDimnames=withDimnames)})

如果任何其他开发人员需要为2021欧洲杯体育投注开户他们自己的类计算负计数,他们可以简单地使用negcounts在包中定义的泛型。

2.5对包装组织的一些看法

按照惯例,所有的类定义(即setClass语句)在一个名为AllClasses。R,所有新的泛型定义在一个名为AllGenerics。R,以及文件中字母数字顺序在前两个之后的所有方法定义。这是因为R在构建包时按字母数字顺序对文件进行排序。对类和泛型进行排序(和定义)是至关重要的之前相应的方法,否则会发生错误。如果字母数字排序不合适,开发人员可以使用2021欧洲杯体育投注开户整理:描述档案-请参阅编写R扩展欲知详情。

3.派生带有自定义槽的类

3.1类定义

在实践中,大多数派生类都需要存储特定于应用程序的数据结构。在本文档的其余部分,我们将考虑派生具有自定义槽的类来保存此类结构。首先,我们考虑1D数据结构:

  • rowVec:每个值与对象的一行1:1映射关系SummarizedExperiment
  • colVec:每个值与对象的一列1:1映射SummarizedExperiment

任何一维结构都可以使用,如果它支持长度c而且(< -.为简单起见,我们将使用整数向量* .vec槽。

我们还考虑了一些2D数据结构:

  • rowToRowMat:每一行1:1映射到存储节点的某一行SummarizedExperiment
  • colToColMat:每列与对象的某列1:1映射SummarizedExperiment
  • rowToColMat对象的每一行到列的1:1映射SummarizedExperiment
  • colToRowMat对象的每一列到一行的1:1映射SummarizedExperiment

任何2D结构都可以使用,只要它支持nrowncolcbindrbind而且(< -.为简单起见,我们将使用(数值)矩阵* .mat槽。

类的定义是使用setClass,使用槽=参数指定新的自定义插槽5重复Roxygen标记没有坏处,它显式地指定了每个类和函数所需的导入。

- setClass("ExampleClass", slots= representation(rowVec="integer", colVec="integer", rowToRowMat="matrix", rowToColMat="matrix", rowToColMat="matrix", colToRowMat="matrix"), contains=" summarizeexperiment ")

3.2定义构造函数

构造函数应该提供一些参数,用于在派生类定义中设置新插槽。应设置默认值,以便在不带任何参数的情况下调用构造函数返回有效值ExampleClass对象。

#' @export #' @importFrom summarizeexperimental summarizeexperimental ExampleClass <- function(rowVec=integer(0), colVec=integer(0), rowToRowMat=matrix(0,0,0), colToColMat=matrix(0,0,0), rowToColMat=matrix(0,0,0), colToRowMat=matrix(0,0,0),…){se <- summarizeexperiment(…).ExampleClass(se, rowVec=rowVec, colVec=colVec, rowToRowMat=rowToRowMat, colToColMat=colToColMat, colToRowMat= colToColMat, colToRowMat=colToRowMat)}

3.3创建getter方法

3.3.1对于1D数据结构

我们为包含1D结构的自定义槽定义了一些getter泛型。

#' @export setGeneric("rowVec", function(x,…)
##[1]“rowVec”
#' @export setGeneric("colVec", function(x,…)
## [1] colVec"

然后为这些泛型定义类特定的方法。注意withDimnames = TRUE参数,该参数强制提取的对象的名称与原始对象的名称保持一致SummarizedExperiment.可以关闭此功能以提高效率,例如,在内部使用时不需要使用名称。

#' @export setMethod("rowVec", "ExampleClass", function(x, withDimnames=TRUE) {out <- x@rowVec if (withDimnames) names(out) <- rownames(x) out}) #' @export setMethod("colVec", "ExampleClass", function(x, withDimnames=TRUE) {out <- x@colVec if (withDimnames) names(out) <- colnames(x) out})

3.3.2对于2D数据结构

我们对2D结构重复这个过程。

#' @export setGeneric("rowToRowMat", function(x,…)
##[1]“rowToRowMat”
#' @export setGeneric("colToColMat", function(x,…)
##[1]“colToColMat”
#' @export setGeneric("rowToColMat", function(x,…)
##[1]“rowToColMat”
#' @export setGeneric("colToRowMat", function(x,…)
##[1]“colToRowMat”

同样,我们为这些泛型定义了类特定的方法。

#' @export setMethod("rowToColMat", "ExampleClass", function(x, withDimnames=TRUE) {out <- x@rowToRowMat if (withDimnames) rownames(out) <- rownames(x) out}) #' @export setMethod("rowToColMat", "ExampleClass", function(x, withDimnames=TRUE) {out <- x@colToColMat if (withDimnames) colnames(out) <- colnames(x) out}) #' @export setMethod("rowToColMat", "ExampleClass", function(x,withDimnames=TRUE) {out <- x@rowToColMat if (withDimnames) rownames(out) <- colnames(x) out}) #' @export setMethod("colToRowMat", "ExampleClass", function(x, withDimnames=TRUE) {out <- x@colToRowMat if (withDimnames) colnames(out) <- rownames(x) out})

3.3.3SummarizedExperiment

中定义的getter方法SummarizedExperiment可以直接用于从基类中的槽中检索数据。这些通常不需要为派生类重新定义。但是,如果有必要,应该使用这些方法callNextMethod在内部。这将调用基类的方法SummarizedExperiment类,其输出可根据需要修改。

#' @export #' @importMethodsFrom summarizeexperiment rowData setMethod("rowData", "ExampleClass", function(x,…){out <- callNextMethod() #在这里做一些额外的事情。out$extra <- runif(nrow(out)) #返回rowData对象。})

3.4定义有效性方法

我们使用setValidity2定义一个有效性函数ExampleClass.我们使用以前定义的getter函数来检索槽值,而不是使用@.将接口与实现分开通常是一个好主意6这可以防止插槽名称的更改,并在存储模式与数据的概念含义不同时简化开发,例如,为了提高效率。.我们还设置了withDimnames = FALSE在我们的getter调用中,内部函数不需要一致的命名。

# ' @importFrom BiocGenerics NCOL NROW setValidity2(“ExampleClass函数(对象){NR < - NROW(对象)数控< - NCOL(对象)味精< -零# 1 d如果(长度(rowVec(对象,withDimnames = FALSE)) ! = NR){味精< - c(味精、”“rowVec”应该长度等于的行数”)}如果(长度(colVec(对象,withDimnames = FALSE)) ! = NC){味精< - c(味精、”“colVec”应该长度等于列数”)}# 2 d如果(NROW (rowToRowMat(对象,withDimnames = FALSE)) ! = NR){味精< - c(味精,“nrow (rowToRowMat)应该等于的行数”)}如果(NCOL (colToColMat(对象,withDimnames = FALSE)) ! = NC){味精< - c(味精、”“NCOL (colToColMat)应该等于列数”)}如果(nrow (rowToColMat(对象,withDimnames = FALSE)) ! = NC){味精< - c(味精、”“nrow (rowToColMat)应该等于列数”)}如果(NCOL (colToRowMat(对象,withDimnames = FALSE)) ! = NR){味精< - c(味精,"'ncol(colToRowMat)'应该等于行数")}if (length(msg)) {msg} else TRUE})
##示例类“ExampleClass”GlobalEnv"] ## ##插槽位:## ##名称:rowVec colVec rowToRowMat colToColMat ##类:整数整数矩阵矩阵## ##名称:rowToColMat colToRowMat colData assays ##类:矩阵矩阵DataFrame Assays_OR_NULL ## ##名称:NAMES elementMetadata元数据##类:character_OR_NULL DataFrame列表## ##扩展:##类“summarizeexperiment”,直接##类“RectangularData”,由类“summarizeexperiment”,距离2 ##类“Vector”,由类“summarizeexperiment”,距离2 ##类“Annotated”,由类“summarizeexperiment”,距离3 ##类“vector_OR_Vector”,由类“summarizeexperiment”,距离3 ##

我们使用NCOL而且NROW方法从BiocGenerics因为这些方法支持各种Bioconductor对象,而基本方法不支持。

3.5创建一个显示方法

默认的显示方法将只显示有关SummarizedExperiment槽。我们可以扩展它来显示自定义槽的一些相关方面。这是通过调用基来实现的显示方法,然后根据需要打印其他字段。

# @export # @importMethodsFrom summarize实验show setMethod("show", "ExampleClass", function(对象){callNextMethod() cat("rowToRowMat has ", ncol(rowToRowMat(对象))," columns\n", "colToColMat has ", nrow(colToColMat(对象))," rows\n", "colToColMat has ", ncol(rowToRowMat(对象))," columns\n", "colToRowMat has ", ncol(rowToRowMat(对象))," rows\n", "colToRowMat has ", ncol(rowToRowMat(对象))," rows\n", "colToRowMat has ", " rows\n", sep="")})

3.6创建setter方法

3.6.1对于1D数据结构

我们为包含1D结构的自定义槽定义了一些setter方法。同样,这通常需要创建新的泛型。

#' @export setGeneric("rowVec<-", function(x,…("rowVec<-"))
"rowVec<-"
#' @export setGeneric("colVec<-", function(x,…("colVec<-"))
## [1] "colVec<-"

我们为这些泛型定义了类特定的方法。注意使用validObject以确保指定的输入仍然有效。

@export setReplaceMethod("rowVec", "ExampleClass", function(x, value) {x@rowVec <- value validObject(x) x}) #' @export setReplaceMethod("colVec", "ExampleClass", function(x, value) {x@colVec <- value validObject(x) x})

操作对于2D数据结构

我们对2D结构重复这个过程。

#' @export setGeneric("rowToRowMat<-", function(x,…("rowToRowMat<-"))
## [1] "rowToRowMat<-"
#' @export setGeneric("colToColMat<-", function(x,…("colToColMat<-"))
## [1] "colToColMat<-"
#' @export setGeneric("rowToColMat<-", function(x,…("rowToColMat<-"))
## [1] "rowToColMat<-"
#' @export setGeneric("colToRowMat<-", function(x,…("colToRowMat<-"))
## [1] "colToRowMat<-"

同样,我们为这些泛型定义了类特定的方法。

#' @export setReplaceMethod("rowToRowMat", "ExampleClass",函数(x,值){x@rowToRowMat <- value validObject(x) x}) #' @export setReplaceMethod("colToColMat", "ExampleClass",函数(x,值){x@colToColMat <- value validObject(x) x}) #' @export setReplaceMethod("rowToColMat", "ExampleClass",函数(x,值){x@rowToColMat <- value validObject(x) x}) #' @export setReplaceMethod("colToRowMat", "ExampleClass",函数(x,值){x@colToRowMat <- value validObject(x) x})

3.6.3SummarizedExperiment

中定义的setter方法SummarizedExperiment来修改基类中的槽。这些通常不需要重新定义。但是,如果有必要,应该使用这些方法callNextMethod内部:

#' @export #' @importMethodsFrom summarizeexperiment "rowData<-" setReplaceMethod("rowData", "ExampleClass", function(x,…, value) {y <- callNextMethod() #返回一个修改后的ExampleClass #在这里做一些额外的事情。消息("hi!\n") y})

3.6.4其他类型的修改函数

假设我们想要编写一个函数,该函数返回一个modifiedExampleClass的符号* .vec字段颠倒了。例如,我们将假装我们想要写一个正常化函数,使用泛型fromBiocGenerics

#' @export #' @importFrom BiocGenerics normalize setMethod("normalize", "ExampleClass", function(object){#做一些令人兴奋的事情,即将符号翻转为新的。row <- - rowvec(对象,withDimnames=FALSE) new。col <- -colVec(object, withDimnames=FALSE) BiocGenerics:::replaceSlots(object, rowVec=new.row, colVec=new.col, check=FALSE) })

我们使用BiocGenerics::: replaceSlots而不是我们上面定义的setter方法。这是因为如果我们知道修改不能改变对象的有效性,那么setter执行的有效性检查是不必要的。的replaceSlots函数允许我们跳过这些有效性检查(检查= FALSE)以提高效率。

3.7启用子操作

3.7.1得到一个子集

一个关键的力量SummarizedExperiment类的特点是跨各个(元)数据字段同步子设置。这避免了簿记错误,并保证了整个交互分析会话的一致性。我们需要确保自定义槽中的值也是子集。

#' @export setMethod("[", "ExampleClass", function(x, i, j, drop=TRUE) {rv <- rowVec(x, withDimnames=FALSE) cv <- colVec(x, withDimnames=FALSE) rrm <- rowToRowMat(x, withDimnames=FALSE) ccm <- colToColMat(x, withDimnames=FALSE) rcm <- colToRowMat(x, withDimnames=FALSE) crm <- colToRowMat(x, withDimnames=FALSE) if (!missing(i)) {if (is.character(i)) {fmt <- paste0("<", class(x), ">[i,] index out of bounds: %s") i <- summarize实验:::。charbound(i, rownames(x), fmt)} i <- as.vector(i) rv <- rv[i] rrm <- rrm[i,,drop=FALSE] crm <- crm[,i,drop=FALSE]} if (!missing(j)) {if (is.character(j)) {fmt <- paste0("<", class(x), ">[,j] index out of bounds: %s") j <- summarizeexperimental:::. summarizeexperimental。charbound( j, colnames(x), fmt ) } j <- as.vector(j) cv <- cv[j] ccm <- ccm[,j,drop=FALSE] rcm <- rcm[j,,drop=FALSE] } out <- callNextMethod() BiocGenerics:::replaceSlots(out, rowVec=rv, colVec=cv, rowToRowMat=rrm, colToColMat=ccm, rowToColMat=rcm, colToRowMat=crm, check=FALSE) })

注意处理字符索引的特殊代码,以及callNextMethod将基子集SummarizedExperiment槽。

3.7.2章分配子集

子集赋值也可以类似地执行,不过需要指定签名以便替换值属于同一类。这通常是替换自定义插槽所必需的。

#' @export setReplaceMethod("[", c("ExampleClass", "ANY", "ANY", "ExampleClass"), function(x, i, j,…,value) { rv <- rowVec(x, withDimnames=FALSE) cv <- colVec(x, withDimnames=FALSE) rrm <- rowToRowMat(x, withDimnames=FALSE) ccm <- colToColMat(x, withDimnames=FALSE) rcm <- rowToColMat(x, withDimnames=FALSE) crm <- colToRowMat(x, withDimnames=FALSE) if (!missing(i)) { if (is.character(i)) { fmt <- paste0("<", class(x), ">[i,] index out of bounds: %s") i <- SummarizedExperiment:::.SummarizedExperiment.charbound( i, rownames(x), fmt ) } i <- as.vector(i) rv[i] <- rowVec(value, withDimnames=FALSE) rrm[i,] <- rowToRowMat(value, withDimnames=FALSE) crm[,i] <- colToRowMat(value, withDimnames=FALSE) } if (!missing(j)) { if (is.character(j)) { fmt <- paste0("<", class(x), ">[,j] index out of bounds: %s") j <- SummarizedExperiment:::.SummarizedExperiment.charbound( j, colnames(x), fmt ) } j <- as.vector(j) cv[j] <- colVec(value, withDimnames=FALSE) ccm[,j] <- colToColMat(value, withDimnames=FALSE) rcm[j,] <- rowToColMat(value, withDimnames=FALSE) } out <- callNextMethod() BiocGenerics:::replaceSlots(out, rowVec=rv, colVec=cv, rowToRowMat=rrm, colToColMat=ccm, rowToColMat=rcm, colToRowMat=crm, check=FALSE) })

3.8定义组合方法

3.8.1由行

我们需要定义arbind方法。这是通过组合跨类实例的自定义逐行槽来实现的。

#' @export setMethod("rbind", "ExampleClass", function(…, leave .level=1) {args <- list(…)rv <- lapply(args, rowVec, withDimnames=FALSE)所有。rrm <- lapply(args, rowToRowMat, withDimnames=FALSE) all。crm <- lapply(args, colToRowMat, withDimnames=FALSE)所有。Rv <- do。call(c, all.rv) all.rrm <- do.call(rbind, all.rrm) all.crm <- do.call(cbind, all.crm) # Checks for identical column state. ref <- args[[1]] ref.cv <- colVec(ref, withDimnames=FALSE) ref.ccm <- colToColMat(ref, withDimnames=FALSE) ref.rcm <- rowToColMat(ref, withDimnames=FALSE) for (x in args[-1]) { if (!identical(ref.cv, colVec(x, withDimnames=FALSE)) || !identical(ref.ccm, colToColMat(x, withDimnames=FALSE)) || !identical(ref.rcm, rowToColMat(x, withDimnames=FALSE))) { stop("per-column values are not compatible") } } old.validity <- S4Vectors:::disableValidity() S4Vectors:::disableValidity(TRUE) on.exit(S4Vectors:::disableValidity(old.validity)) out <- callNextMethod() BiocGenerics:::replaceSlots(out, rowVec=all.rv, rowToRowMat=all.rrm, colToRowMat=all.crm, check=FALSE) })

我们检查所有元素的其他每列槽,以确保它们相同。这可以防止用户组合不兼容的对象。但是,根据应用程序的不同,对于所有插槽来说,这可能不是必需的(或者代价太大),在这种情况下,可以将其限制在关键插槽。

我们也使用disableValidity避免在基地中进行有效性检查的方法cbind方法。这是因为从技术上讲,当组合基本槽时,但在使用自定义槽的新组合值更新对象之前,该对象是无效的。的on.exit调用确保在函数退出时恢复原来的有效性设置。

3.8.2通过列

我们同样定义了cbind方法来处理自定义插槽。

#' @export setMethod("cbind", "ExampleClass", function(…, leave .level=1) {args <- list(…)cv <- lapply(args, colVec, withDimnames=FALSE) all.ccm <- lapply(args, colToColMat, withDimnames=FALSE) all.rcm <- lapply(args, rowToColMat, withDimnames=FALSE) all.cv <- do.call(c, all.cv) all.ccm <- do.call(cbind, all.ccm) all.rcm <- do.call(rbind, all.rcm) # Checks for identical column state. ref <- args[[1]] ref.rv <- rowVec(ref, withDimnames=FALSE) ref.rrm <- rowToRowMat(ref, withDimnames=FALSE) ref.crm <- colToRowMat(ref, withDimnames=FALSE) for (x in args[-1]) { if (!identical(ref.rv, rowVec(x, withDimnames=FALSE)) || !identical(ref.rrm, rowToRowMat(x, withDimnames=FALSE)) || !identical(ref.crm, colToRowMat(x, withDimnames=FALSE))) { stop("per-row values are not compatible") } } old.validity <- S4Vectors:::disableValidity() S4Vectors:::disableValidity(TRUE) on.exit(S4Vectors:::disableValidity(old.validity)) out <- callNextMethod() BiocGenerics:::replaceSlots(out, colVec=all.cv, colToColMat=all.ccm, rowToColMat=all.rcm, check=FALSE) })

3.9定义强制方法

3.9.1强迫的SummarizedExperiment

我们定义了一个强制的方法SummarizedExperiment对象到我们的ExampleClass类。

#' @exportMethods coerce setAs(" summarize实验","ExampleClass", function(from) {new("ExampleClass", from, rowVec=integer(nrow(from)), colVec=integer(ncol(from)), rowToRowMat=矩阵(0,nrow(from),0), colToColMat=矩阵(0,0,ncol(from))), colToRowMat=矩阵(0,ncol(from),0), colToRowMat=矩阵(0,0,ncol(from)))})

这和预期的一样:

se <- summary izeexperiment (matrix(rpois(100, lambda=1), ncol=5)) as(se, "CountSE")
##类:CountSE ## dim: 20 5 ##元数据(0):## assays(1): " ## rownames: NULL ## rowData names(0): ## colnames: NULL ## colData names(0):

这在我们前面的例子中并不是必须的CountSE未添加新插槽时初始化。当然,开发人员仍然可以2021欧洲杯体育投注开户显式地编写一个转换方法来执行额外的工作,以实现“合理的”转换——例如,可以取第一个矩阵的所有项的绝对值,以确保CountSE对所有输入有效吗SummarizedExperiment对象。

3.9.2RangedSummarizedExperiment

注意,如果我们从a推导RangedSummarizedExperiment例:对某些人来说ExampleClassRanged),则有必要从两者定义显式转换RangedSummarizedExperiment而且SummarizedExpermentExampleClassRanged.理论上,我们只需要定义从的转换RangedSummarizedExperimentExampleClassRanged-然后,任何试图从SummarizedExperimentExampleClassRanged将:

  1. 使用现有的SummarizedExperimentRangedSummarizedExperiment定义在SummarizedExperiment,然后
  2. 使用新的RangedSummarizedExperimentExampleClassRanged我们刚刚定义的转换器。

不幸的是,在涉及到非直接子类的转换的情况下,S4系统会自动为没有显式定义的任何转换创建方法。的方法进行转换时,不会使用上面列出的正确的方法“链”SummarizedExperiment到一个ExampleClassRanged对象。取而代之的是自动生成的方法,当转换的细节被忽略时,它可能不会产生有效的对象。我们通过显式地为两者定义转换器来避免这种情况SummarizedExperiment而且RangedSummarizedExperimentExampleClassRanged

4单元测试程序

4.1概述

测试我们的新方法expect_ *函数从testthat包中。每个函数都将测试一个表达式,如果输出不符合预期,则将引发错误。属性的单元测试可用于构造单元测试测试/包的子目录。单元测试确保方法的行为符合预期,特别是在将来可能执行的任何重构之后。

的实例进行测试ExampleClass它有10行7列

RV <- 1:10 CV <- sample(50,7) RRM <- matrix(runif(30), nrow=10) CCM <- matrix(rnorm(14), ncol=7) RCM <- matrix(runif(21), ncol=7) CRM <- matrix(rnorm(20), ncol=10) thing <- ExampleClass(rowVec=RV, colVec=CV, rowToRowMat=RRM, colToColMat=CCM, rowToColMat=RCM, colToRowMat=CRM, assays=list(counts=matrix(rnorm(70), nrow=10), colData=DataFrame(whee=LETTERS[1:7]), rowData=DataFrame(yay= LETTERS[1:10])))

我们还将添加一些行名和列名,这将在以后派上用场。

rownames(thing) <- paste0("FEATURE_", seq_len(nrow(thing))) colnames(thing) <- paste0("SAMPLE_", seq_len(ncol(thing))) thing
##类:exampleeclass ## dim: 10 7 ##元数据(0):## assays(1):计数## rownames(10): FEATURE_1 FEATURE_2…FEATURE_9 FEATURE_10 ## rowData names(2): yay extra ## colnames(7): SAMPLE_1 SAMPLE_2…SAMPLE_6 SAMPLE_7 ## colData名称(1):whe# # rowToRowMat有3列## colToColMat有2行## rowToColMat有3列## colToRowMat有3行

4.2构造函数

我们测试事情构造的对象是有效的:

expect_true (validObject(的事)

另一组有用的单元测试包括检查默认构造函数(内部的和导出的)是否产生有效对象:

expect_true(validObject(.ExampleClass())) #内部expect_true(validObject(ExampleClass())) #导出

我们还可以验证有效性方法在无效对象上失败:

expect_error(ExampleClass(rowVec=1), "rowVec") expect_error(ExampleClass(colVec=1), "colVec") expect_error(ExampleClass(rowToRowMat=rbind(1)), "rowToRowMat") expect_error(ExampleClass(rowToColMat=rbind(1)), "rowToColMat") expect_error(ExampleClass(rowToColMat=rbind(1)), "rowToColMat") expect_error(ExampleClass(colToRowMat=rbind(1)), "rowToColMat") expect_error(ExampleClass(colToRowMat=rbind(1)), "colToRowMat") expect_error(ExampleClass(colToRowMat=rbind(1)), "colToRowMat")

最后,检查强制方法产生的对象是否有效。

se <- as(thing, " summarizeexperiment ") conv <- as(se, "ExampleClass") expect_true(validObject(conv))

4.3getter

测试1D getter方法:

expect_same (names(rowVec(thing)), rownames(thing)) expect_alike (rowVec(thing, withDimnames=FALSE), RV) expect_alike (names(colVec(thing)), colnames(thing)) expect_alike (colVec(thing, withDimnames=FALSE), CV)

测试2D getter方法:

expect_identical (rowToRowMat(东西,withDimnames = FALSE), RRM) expect_identical (rownames (rowToRowMat(的事),rownames(东西))expect_identical (colToColMat(东西,withDimnames = FALSE), CCM) expect_identical (colnames (colToColMat(的事),colnames(东西))expect_identical (rowToColMat(东西,withDimnames = FALSE), RCM) expect_identical (rownames (rowToColMat(的事),colnames(东西))expect_identical (colToRowMat(东西,withDimnames = FALSE), CRM) expect_identical (colnames (colToRowMat(东西)),rownames(东西))

测试定制rowData方法:

expect_true("extra" %in% colnames(rowData(thing)))

4.4setter

测试1D setter方法:

rowVec(thing) <- 0:9 expect_equivalent(rowVec(thing), 0:9) colVec(thing) <- 7:1 expect_equivalent(colVec(thing), 7:1)

测试2D setter方法:

old <- rowToRowMat(东西)rowToRowMat(东西)<- old expect_equivalent(rowToRowMat(东西),-old) old <- colToColMat(东西)colToColMat(东西),2 * old colToColMat(东西)old <- rowToColMat(东西)rowToColMat(东西)<- old + 1 expect_equivalent(rowToColMat(东西),old + 1) old <- colToRowMat(东西)colToRowMat(东西)<- old expect_equivalent(colToRowMat(东西),old / 10 expect_equivalent(colToRowMat(东西),old / 10)

测试我们的定制rowData < -方法:

expect_message(rowData(thing) <- 1, "hi")

我们确保我们可以成功地触发有效性方法上的错误:

expect_error(rowVec(thing) <- 0, "rowVec") expect_error(colVec(thing) <- 0, "colVec") expect_error(rowToRowMat(thing) <- rbind(0), "rowToRowMat") expect_error(colToColMat(thing) <- rbind(0), "colToColMat") expect_error(rowToColMat(thing) <- rbind(0), "rowToColMat") expect_error(colToRowMat(thing) <- rbind(0), "rowToColMat") expect_error(colToRowMat(thing) <- rbind(0), "rowToColMat") expect_error(colToRowMat(thing) <- rbind(0), "colToRowMat")

4.5其他修改功能

我们测试我们的正常化方法:

modified <- normalize(thing) expect_equal(rowVec(modified), -rowVec(thing)) expect_equal(colVec(modified), -colVec(thing))

4.6构造子集的方法

按行细分:

subbyrow <- thing[1:5,] expect_same (rowVec(subbyrow), rowVec(thing)[1:5]) expect_same (rowToRowMat(subbyrow), rowToRowMat(thing)[1:5,]) expect_same (colToRowMat(subbyrow), colToRowMat(thing)[,1:5]) # columns不受影响…expect_alike (colVec(subbyrow), colVec(thing)) expect_alike (colToColMat(subbyrow), colToColMat(thing)) expect_alike (rowToColMat(subbyrow), rowToColMat(thing))

按列细分:

subbycol <- thing[,1:2] expect_same (colVec(subbycol), colVec(thing)[1:2]) expect_same (colToColMat(subbycol), colToColMat(thing)[,1:2]) expect_same (rowToColMat(subbycol), rowToColMat(thing)[1:2,]) # rows不受影响…expect_alike (rowVec(subbycol), rowVec(thing)) expect_alike (rowToRowMat(subbycol), rowToRowMat(thing)) expect_alike (colToRowMat(subbycol), colToRowMat(thing))

检查该子设置创建一个空对象是可能的:

norow <- thing[0,] expect_true(validObject(norow)) expect_same (nrow(norow), 0L) nocol <- thing[,0] expect_true(validObject(nocol)) expect_same (ncol(nocol), 0L)

子集的任务:

修改< -修改(1:5,1:2)< -东西[5:1,2:1]rperm < - c (5:1, 6: nrow(东西))expect_identical (rowVec(修改),rowVec(事)[rperm]) expect_identical (rowToRowMat(修改),rowToRowMat(事)[rperm,]) expect_identical (colToRowMat(修改),colToRowMat(事)[,rperm]) cperm < - c (2:1, 3: ncol(东西))expect_identical (colVec(修改),colVec(事)[cperm]) expect_identical (colToColMat(修改),colToColMat(事)[,cperm]) expect_identical (rowToColMat(修改后的),rowToColMat(件)(cperm,))

检查我们在平凡赋值操作后获得相同的对象:

修改<- thing Modified [0,] <- thing[0,] expect_equal(Modified, thing) Modified [1,] <- thing[1,] expect_equal(Modified, thing) Modified [,0] <- thing[,0] expect_equal(Modified, thing) Modified [,1] <- thing[,1] expect_equal(Modified, thing)

我们仔细检查是否可以在无效赋值时得到错误:

Expect_error (modified[1,1] <- thing[0,0], "replace has length 0 ")

4.7结合的方法

按行组合:

combined <- rbind(thing, thing) rtwice <- rep(seq_len(nrow(thing)), 2) expect_same (rowVec(combined), rowVec(thing)[rtwice]) expect_same (rowToRowMat(combined), rowToRowMat(thing)[rtwice]) expect_same (colToRowMat(combined), colToRowMat(thing)[,rtwice])

按列组合。我们使用test_equivalent为了简单起见,这里修改了列名以保持惟一性。

combined <- cbind(thing, thing) ctwice <- rep(seq_len(ncol(thing)), 2) expect_equivalent(colVec(combined), colVec(thing)[ctwice]) expect_equivalent(colToColMat(combined), colToColMat(thing)[,ctwice]) expect_equivalent(rowToColMat(combined), rowVec(thing) [ctwice,]) expect_equivalent(rowToColMat(combined), rowVec(thing)) expect_equivalent(rowToRowMat(combined), rowToRowMat(thing)) expect_equivalent(colToRowMat(combined), colToRowMat(thing))

检查合并单个对象或空对象是否得到相同的对象:

Expect_equal (thing, rbind(thing, thing[0,])) Expect_equal (thing, rbind(thing, thing[0,])) Expect_equal (thing, cbind(thing, thing[,0]))

检查兼容性错误是否被正确抛出:

Expect_error (rbind(thing, thing[,ncol(thing):1]), "不兼容")Expect_error (cbind(thing, thing[nrow(thing):1,]), "不兼容")

5文档

我们建议至少创建两个独立的文档(例如:*。理查德·道金斯)文件。第一个文件将记录类和构造函数:

\name{ExampleClass类}\alias{ExampleClass-class} \alias{ExampleClass} \title{ExampleClass类}\description{ExampleClass类和构造函数的概述。} \usage{ExampleClass(rowVec=integer(0), colVec=integer(0), #等等,等等,我不会在这里全部写出来。){\arguments{\item{rowVec}{一个映射到行的整数向量,表示一些重要的东西。}\item{colVec}{An integer vector mapping to the columns, representing something else that's important.} % And so on... } \details{ % Some context on why this class and its slots are necessary. The ExampleClass provides an example of how to derive from the SummarizedExperiment class. Its slots have no scientific meaning and are purely for demonstration purposes. }

第二个文件将记录所有单独的方法:

\name{ExampleClass methods} %新泛型:\alias{rowVec} \alias{rowVec,ExampleClass-method} \alias{rowVec<-} \alias{rowVec<-,ExampleClass-method} %%等等…已经有一个泛型:\alias{[,ExampleClass-method} \alias{[,ExampleClass,ANY-method} \alias{[,ExampleClass,ANY,ANY-method} \alias{rbind,ExampleClass-method} %%等等…\title{ExampleClass方法}\description{ExampleClass类的方法。}\usage{ \S4method{rowVec}{ExampleClass}(x, withDimnames=FALSE) \S4method{rowVec}{ExampleClass}(x) <- value \S4method{[}{ExampleClass}(x, i, j, drop=TRUE) \S4method{rbind}{ExampleClass}(..., , i, j, drop=TRUE) %% And so on... } \arguments{ \item{x}{An ExampleClass object.} \item{withDimnames}{A logical scalar indicating whether dimension names from \code{x} should be returned.} \item{value}{ For \code{rowVec}, an integer vector of length equal to the number of rows. For \code{colVec}, an integer vector of length equal to the number of columns. } %% And so on... } \section{Accessors}{ % Add some details about accessor behaviour here. } \section{Subsetting}{ % Add some details about subsetting behaviour here. } \section{Combining}{ % Add some details about combining behaviour here. }

6会话信息

sessionInfo ()
## R版本4.1.0(2021-05-18)##平台:x86_64-pc-linux-gnu(64位)##运行在:Ubuntu 20.04.2 LTS ## ##矩阵产品:默认## BLAS: /home/biocbuild/bbs-3.13-bioc/R/lib/libRblas。所以## LAPACK: /home/biocbuild/bbs-3.13-bioc/R/lib/libRlapack。所以## ## locale: ## [1] LC_CTYPE=en_US。UTF-8 LC_NUMERIC= c# # [3] LC_TIME=en_GB LC_COLLATE= c# # [5] LC_MONETARY=en_US。utf - 8 LC_MESSAGES = en_US。UTF-8 ## [7] LC_PAPER=en_US。UTF-8 LC_NAME= c# # [9] LC_ADDRESS=C lc_phone = c# # [11] LC_MEASUREMENT=en_US。utf - 8 LC_IDENTIFICATION = C附加基本包:# # # # # #[1]平行stats4统计图形grDevices跑龙套数据集# #[8]方法基础# # # #其他附加包:# # [1]testthat_3.0.2 SummarizedExperiment_1.22.0 # # [3] Biobase_2.52.0 GenomicRanges_1.44.0 # # [5] GenomeInfoDb_1.28.0 IRanges_2.26.0 # # [7] S4Vectors_0.30.0 BiocGenerics_0.38.0 # # [9] MatrixGenerics_1.4.0 matrixStats_0.58.0 # # [11] BiocStyle_2.20.0 # # # #通过加载一个名称空间(而不是附加):## [1] bslib_0. 2.0.1 BiocManager_1.30.15 ## [4] jquerylib_0.1.4 XVector_0.32.0 bitops_1.0-7 ## [7] tools_4.1.0 zlibbioc_1.38.0 pkgload_1.2.1 ## [10] digest_0.6.27 jsonlite_1.7.2 evaluate_0.14 ## [13] lattice_0.20-44 rlang_0.4.11 Matrix_1.3-3 ## [13] DelayedArray_0.18.0 yaml_2.2.1 xfun_0.23 ## [19] GenomeInfoDbData_1.2.6 withr_2.4.2 string_1 .4.0 ## [28] R6_2.5.0 rmarkdown_2.8 bookdown_0.22 ## #[31] waldo_0.2.5 magrittr_2.0.1 htmltools_0.5.1.1 ## [34] stringi_1.6.2 RCurl_1.98-1.3 crayon_1.4.1