1动机

大量Bioconductor包包含扩展的标准SummarizedExperiment类的SummarizedExperiment包中。这使得开发人员可以利用的bob电竞体育官网力量SummarizedExperiment同步数据和元数据的表示,同时还容纳专门为特定的科学应用程序数据结构。本文旨在提供一个developer-level“最佳实践”参考这些派生类的创建。

2推导一个简单的类

2.1概述

介绍各种概念,我们将开始一个简单的派生类,不添加任何新的插槽。这是额外的约束时偶尔有用需要被放置在派生类。在这个例子中,我们假设我们希望我们班举行一个最小“计数”分析包含非负价值1为简单起见,我们不会担心执行整数类型,作为分数值是可能的,例如,在处理预期的数量。

2.2定义类和它的构造函数

我们名字的新类CountSE和使用定义它setClass函数的方法包,作为所有S4类是传统。我们使用Roxygen的#”标记触发导入/导出报表的生成名称空间我们的包。

# @export # # @ import方法”@importClassesFrom SummarizedExperiment SummarizedExperiment .CountSE < - setClass (“CountSE”,包含=“SummarizedExperiment”)

我们定义一个构造函数,该函数接受一个矩阵来创建一个计数CountSE对象。我们使用通过进一步的论据SummarizedExperiment构造函数,它可以让我们避免测量所有的参数。

#“@export #”@importFrom SummarizedExperiment SummarizedExperiment CountSE < -函数(计数,…){se < - SummarizedExperiment(列表(数量=计数),…).CountSE (se)}

2.3定义一个方法的有效性

我们定义一个有效方法,强制约束,我们前面所述。这是通过定义一个函数使用有效性setValidity2S4Vectors2这让我们关掉有效性检查在中间对象的内部函数可能不是有效的函数的范围内。。返回一个字符串显示,有一个问题和触发器R会话中的一个错误。

setValidity2 (“CountSE函数(对象){味精< - NULL如果(assayNames(对象)[1]! =“计数”){味精< - c(味精,必须首先分析“计数”)}如果(min(化验(对象)< 0){味精< - c(味精、计数”中的“负)}如果(is.null(味精)){真}其他味精})
(在“CountSE # #类。GlobalEnv“# # # #槽:# # # #名称:colData化验名字elementMetadata # #类:DataFrame Assays_OR_NULL character_OR_NULL DataFrame # # # #名:元数据类:# # # # # #扩展列表:# #阶级”SummarizedExperiment”,直接# #类“RectangularData”,按类“SummarizedExperiment”,距离2 # #类“向量”,按类“SummarizedExperiment”,距离2 # #类“注释”,按类“SummarizedExperiment”,距离3 # #类“vector_OR_Vector”,按类“SummarizedExperiment”,距离3

构造函数的收益率预期输出计数时提供:

CountSE(矩阵(rpois(100,λ= 1),ncol = 5))
# # # #类:CountSE暗淡:20 5 # #元数据(0):# #化验(1):计数# # rownames:零构成了rowData名称(0):# # # # colnames:零# # colData名称(0):

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

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

2.4定义一个getter方法

一个通用的函数是一组具有相同名称的操作在不同的类。在一个对象上调用通用,S4调度系统将选择最适当的函数使用基于对象类。这允许用户和开发人员编写代码无关的输入类的类bob电竞体育官网型。

假设它是获得特定的科学感兴趣的数量与一个翻转的迹象。我们观察到没有现有的仿制药,做这个任务,例如,在BiocGenericsS4Vectors3如果你有一个想法一个普遍适用的通用,还不可用,请联系Bioconductor核心团队。。相反,我们定义一个新的通用negcounts:

# ' @export setGeneric (“negcounts函数(x,…) standardGeneric (“negcounts”))
# # [1]“negcounts”

然后,我们为我们定义一个特定的方法CountSE4在泛型函数定义意味着自定义参数withDimnames =可以提供特定的方法,如果必要的。:

#“@export #”@importFrom SummarizedExperiment化验setMethod (“negcounts”、“CountSE”,函数(x, withDimnames = TRUE){化验(x, withDimnames = withDimnames)})

如果任何其他开发人员需要计bob电竞体育官网算-计数为自己的课程,他们可以简单地使用negcounts一般定义在我们的包。

2.5一些评论包组织

惯例是把所有类定义(即。,setClass文件命名的语句)AllClasses.R在文件命名,所有新通用的定义AllGenerics.R字母数字混合,所有方法定义在文件要求低于前两个。这是因为R由字母数字顺序整理文件在构建方案。关键是排序(定义)和泛型类的发生之前相应的方法,否则将会出现错误。如果字母数字排序是不合适的,开发人员可以手动指定排序顺序使用bob电竞体育官网整理:描述文件,请参见编写R扩展为更多的细节。

3派生一个类与自定义槽

3.1类定义

在实践中,大多数派生类都需要存储特定于应用程序的数据结构。对于本文档的其余部分,我们将考虑到派生类的自定义槽等结构。首先,我们考虑一维数据结构:

  • rowVec:1:1的映射每个值的行SummarizedExperiment
  • colVec:1:1的映射每个值的列SummarizedExperiment

可以使用任何1 d结构是否支持长度,c,((< -。为简单起见,我们将使用整数向量* .vec槽。

我们也考虑一些二维数据结构:

  • rowToRowMat:1:1映射从每一行一行的SummarizedExperiment
  • colToColMat:1:1的每一列一列的映射SummarizedExperiment
  • rowToColMat:1:1的映射每行的列SummarizedExperiment
  • colToRowMat:1:1映射从每一列的行SummarizedExperiment

可以使用任何二维结构是否支持nrow,ncol,cbind,rbind,((< -。为简单起见,我们将使用(数字)的矩阵* .mat槽。

类的定义是通过使用setClass,使用槽=参数指定新的自定义槽5坏处也没有重复Roxygen标记,它显式地指定为每个类和函数所需的进口。

# @export # # @ import方法”@importClassesFrom SummarizedExperiment SummarizedExperiment .ExampleClass < - setClass (“ExampleClass槽=表示(rowVec =“整数”,colVec =“整数”,rowToRowMat =“矩阵”,colToColMat =“矩阵”,rowToColMat =“矩阵”,colToRowMat =“矩阵”),包含=“SummarizedExperiment”)

3.2定义构造函数

构造函数应该提供一些参数设置新的插槽派生类的定义。默认值应该设置这样调用构造函数不带任何参数返回一个有效ExampleClass对象。

#“@export #”@importFrom SummarizedExperiment SummarizedExperiment ExampleClass < -函数(rowVec =整数(0),colVec =整数(0),rowToRowMat =矩阵(0,0,0),colToColMat =矩阵(0,0,0),rowToColMat =矩阵(0,0,0),colToRowMat =矩阵(0,0,0),…){se < - SummarizedExperiment (…) .ExampleClass (se, rowVec = rowVec colVec = colVec rowToRowMat = rowToRowMat colToColMat = colToColMat rowToColMat = rowToColMat colToRowMat = colToRowMat)}

3.3创建getter方法

3.3.11 d的数据结构

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

# ' @export setGeneric (“rowVec函数(x,…) standardGeneric (“rowVec”))
# # [1]“rowVec”
# ' @export setGeneric (“colVec函数(x,…) standardGeneric (“colVec”))
# # [1]“colVec”

然后,我们为这些泛型定义特定于类的方法。注意withDimnames = TRUE参数,执行一致性提取的对象和原始的名字SummarizedExperiment。可以关掉这个更大的效率,例如,供内部使用,名字不是必要的。

# ' @export setMethod (“rowVec”、“ExampleClass”,函数(x, withDimnames = TRUE) {< - x@rowVec如果(withDimnames)名称()< - rownames (x)}) # ' @export setMethod (“colVec”、“ExampleClass”,函数(x, withDimnames = TRUE) {< - x@colVec如果(withDimnames)名称()< - colnames (x)})

3.3.2对二维数据结构

我们重复这个过程的二维结构。

# ' @export setGeneric (“rowToRowMat函数(x,…) standardGeneric (“rowToRowMat”))
# # [1]“rowToRowMat”
# ' @export setGeneric (“colToColMat函数(x,…) standardGeneric (“colToColMat”))
# # [1]“colToColMat”
# ' @export setGeneric (“rowToColMat函数(x,…) standardGeneric (“rowToColMat”))
# # [1]“rowToColMat”
# ' @export setGeneric (“colToRowMat函数(x,…) standardGeneric (“colToRowMat”))
# # [1]“colToRowMat”

再一次,我们为这些泛型定义特定于类的方法。

# ' @export setMethod (“rowToRowMat”、“ExampleClass”,函数(x, withDimnames = TRUE) {< - x@rowToRowMat如果(withDimnames) rownames () < - rownames (x)}) # ' @export setMethod (“colToColMat”、“ExampleClass”,函数(x, withDimnames = TRUE) {< - x@colToColMat如果(withDimnames) colnames () < - colnames (x)}) # ' @export setMethod (“rowToColMat”、“ExampleClass”,函数(x, withDimnames = TRUE) {< - x@rowToColMat如果(withDimnames) rownames () < - colnames (x)}) # ' @export setMethod (“colToRowMat”、“ExampleClass”,函数(x, withDimnames = TRUE) {< - x@colToRowMat如果(withDimnames) colnames () < - rownames (x)})

3.3.3SummarizedExperiment

中定义的getter方法SummarizedExperiment可以直接用于检索数据从槽的基类。这些通常不需要任何派生类求变。然而,如果它是必要的,应当使用的方法callNextMethod在内部。这将调用该方法的基础SummarizedExperiment的输出类,可以根据需要修改。

#“@export #”@importMethodsFrom构成了rowData setMethod SummarizedExperiment(构成了rowData”“、“ExampleClass”函数(x,…) {< - callNextMethod() #做额外的事情。额外的< - runif美元(nrow()) #构成了rowData对象返回。})

3.4定义有效性的方法

我们使用setValidity2定义有效性函数ExampleClass。我们使用前面定义的getter函数来检索位置值,而不是使用@。这通常是一个好主意保持接口与实现分离6这防止改变槽的名字,简化了开发当存储模式不同于概念意义的数据,例如,对效率的目的。。我们还设置withDimnames = FALSEgetter调用,为内部函数一致的命名是没有必要的。

# ' @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)应该等于的行数”)}如果(长度(味精)){味精}其他真正})
(在“ExampleClass # #类。GlobalEnv“# # # #槽:# # # #名称:rowVec colVec rowToRowMat colToColMat # #类:整数整数矩阵矩阵# # # #名称:rowToColMat colToRowMat colData化验# #类:矩阵矩阵DataFrame Assays_OR_NULL # # # #名称:名称elementMetadata # #元数据类:character_OR_NULL DataFrame列表# # # #延伸:# #阶级”SummarizedExperiment”,直接# #类“RectangularData”,按类“SummarizedExperiment”,距离2 # #类“向量”,按类“SummarizedExperiment”,距离2 # #类“注释”,按类“SummarizedExperiment”,距离3 # #类“vector_OR_Vector”,按类“SummarizedExperiment”,距离3

我们使用NCOLNROW方法从BiocGenerics这些支持各种Bioconductor对象,而基本方法不。

3.5创建一个显示方法

默认的显示方法将只显示信息SummarizedExperiment槽。我们可以增加它显示一些有关方面的自定义槽。这是通过调用基础显示方法在印刷之前附加字段。

#“@export #”@importMethodsFrom SummarizedExperiment显示setMethod(“秀”,“ExampleClass”,函数(对象){callNextMethod()猫(“rowToRowMat”ncol (rowToRowMat(对象),“列\ n”,“colToColMat”nrow (colToColMat(对象),“\ n行”,“rowToColMat”ncol (rowToRowMat(对象),“列\ n”,“colToRowMat”ncol (rowToRowMat(对象),“行\ n”, 9 = " ")})

3.6创建setter方法

3.6.11 d的数据结构

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

# ' @export setGeneric (“rowVec < -函数(x,…,值)standardGeneric (rowVec < -))
# # [1]“rowVec < - - - - - -”
# ' @export setGeneric (“colVec < -函数(x,…,值)standardGeneric (colVec < -))
# # [1]“colVec < - - - - - -”

我们为这些泛型定义特定于类的方法。注意,使用validObject确保分配输入仍是有效的。

# ' @export setReplaceMethod (“rowVec”、“ExampleClass”,函数(x,价值){x@rowVec < -价值validObject x (x)}) # ' @export setReplaceMethod (“colVec”、“ExampleClass”,函数(x,价值){x@colVec < -价值validObject x (x)})

操作对二维数据结构

我们重复这个过程的二维结构。

# ' @export setGeneric (“rowToRowMat < -函数(x,…,值)standardGeneric (rowToRowMat < -))
# # [1]“rowToRowMat < - - - - - -”
# ' @export setGeneric (“colToColMat < -函数(x,…,值)standardGeneric (colToColMat < -))
# # [1]“colToColMat < - - - - - -”
# ' @export setGeneric (“rowToColMat < -函数(x,…,值)standardGeneric (rowToColMat < -))
# # [1]“rowToColMat < - - - - - -”
# ' @export setGeneric (“colToRowMat < -函数(x,…,值)standardGeneric (colToRowMat < -))
# # [1]“colToRowMat < - - - - - -”

再一次,我们为这些泛型定义特定于类的方法。

# ' @export setReplaceMethod (“rowToRowMat”、“ExampleClass”,函数(x,价值){x@rowToRowMat < -价值validObject x (x)}) # ' @export setReplaceMethod (“colToColMat”、“ExampleClass”,函数(x,价值){x@colToColMat < -价值validObject x (x)}) # ' @export setReplaceMethod (“rowToColMat”、“ExampleClass”,函数(x,价值){x@rowToColMat < -价值validObject x (x)}) # ' @export setReplaceMethod (“colToRowMat”、“ExampleClass”,函数(x,价值){x@colToRowMat < -价值validObject x (x)})

3.6.3SummarizedExperiment

再一次,我们可以使用setter方法中定义SummarizedExperiment修改基类中的槽。这些通常不需要任何求变。然而,如果它是必要的,应当使用的方法callNextMethod内部:

构成了rowData < #“@export #”@importMethodsFrom SummarizedExperiment”——“setReplaceMethod(构成了rowData”“、“ExampleClass”函数(x,…值){y < - callNextMethod() #返回一个修改ExampleClass #做额外的事情。消息(“嗨! \ n”) y})

3.6.4其他类型的修改功能

想象一下,我们想写一个函数,返回一个修改ExampleClass的迹象,例如* .vec字段是相反的。例如,我们会假装我们想写一个正常化功能,使用通用的BiocGenerics

#“@export #”@importFrom BiocGenerics setMethod正常化(“正常化”、“ExampleClass”,函数(对象){#做一些令人兴奋的。,把新迹象。行< - -rowVec(对象,withDimnames = FALSE)。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”、功能下降(x, i, j = TRUE){房车< - 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)如果(!失踪(i)) {(is.character(我)){fmt < - paste0(“<”类(x),“>[我]索引越界:% s”)我< - SummarizedExperiment::: .SummarizedExperiment。charbound(我rownames (x), fmt)}我< - as.vector (i)房车< - rv[我]rrm < - rrm(我,放弃= FALSE) crm < - crm(,我滴= FALSE)}如果(!失踪(j)) {(is.character (j)) {fmt < - paste0(“<”类(x),“> [j]索引越界:% s”) < - SummarizedExperiment::: .SummarizedExperiment j。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”,“任何”,“任何”、“ExampleClass”),函数(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由行

我们需要定义一个rbind我们的自定义类的方法。这是通过结合自定义在类实例进行槽。

# ' @export setMethod (“rbind”、“ExampleClass”,函数(…deparse.level = 1) {args < -列表(…)。房车< -拉普(args, rowVec withDimnames = FALSE)。rrm < -拉普(args, rowToRowMat withDimnames = FALSE)。crm < -拉普(args, colToRowMat withDimnames = FALSE)。房车< -。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”,函数(…deparse.level = 1) {args < -列表(…)。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强迫刚毛(“SummarizedExperiment”、“ExampleClass”,函数(){新(“ExampleClass”,从rowVec =整数(nrow(从),colVec =整数(ncol(从),rowToRowMat =矩阵(0,nrow(从),0),colToColMat =矩阵(0,0,ncol(从),rowToColMat =矩阵(0,ncol(从),0),colToRowMat =矩阵(0,0,nrow(从)))})

…这是预期:

se < - SummarizedExperiment(矩阵(rpois(100,λ= 1),ncol = 5)), (se,“CountSE”)
# # # #类:CountSE暗淡:20 5 # #元数据(0):# #化验(1):“# # rownames:零构成了rowData名称(0):# # # # colnames:零# # colData名称(0):

这不是严格我们之前所必需的CountSE类没有添加新的插槽。当然,开发者仍然可以显bob电竞体育官网式编写转换方法执行额外的工作来实现一个“明智的”转换——例如,一个可能需要第一个矩阵的绝对所有条目的值,以确保CountSE所有的输入是有效的吗SummarizedExperiment对象。

3.9.2源于一个RangedSummarizedExperiment

注意,如果我们推导的RangedSummarizedExperiment(例如,对于一些ExampleClassRanged),它会从两个必要定义显式转换RangedSummarizedExperimentSummarizedExpermentExampleClassRanged。从理论上讲,我们应该只需要定义一个转换RangedSummarizedExperimentExampleClassRanged——然后,任何试图转换从一个SummarizedExperimentExampleClassRanged将:

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

不幸的是,在涉及间接转换成子类,S4系统自动创建的任何转换的方法没有明确的定义。这意味着正确的方法不使用上面列出的“链”,当从一个转换SummarizedExperiment到一个ExampleClassRanged对象。自动生成的方法相反,这可能不会产生一个有效的对象时转换的细节被忽略了。我们避免这种场景通过显式地定义两个转换器SummarizedExperimentRangedSummarizedExperimentExampleClassRanged

4单元测试过程

4.1概述

我们测试使用的新方法expect_ *函数的testthat包中。每个函数将测试一个表达式和报错并不如预期的输出。这可以用来构造单元测试的测试/子目录的包。单元测试确保方法的行为符合预期时,特别是在任何可能在未来执行的重构。

测试中,我们将构建的一个实例ExampleClass有10行和7列:

房车< - 1:10 CV < -样例(50,7)RRM < -矩阵(runif (30), nrow = 10) CCM < -矩阵(rnorm (14), ncol = 7) RCM < -矩阵(runif (21), nrow = 7) CRM < -矩阵(rnorm (20), ncol = 10)的< - ExampleClass (rowVec =房车,colVec =简历,rowToRowMat = RRM colToColMat = CCM, rowToColMat = RCM colToRowMat = CRM,化验=列表(数量=矩阵(rnorm (70), nrow = 10)), colData = DataFrame(哟=信[1:7]),rowData = DataFrame(耶=信[1:10]))

我们还将添加一些行和列的名称,后面我们将派上用场。

rownames(事)< - paste0 (“FEATURE_ seq_len (nrow(东西)))colnames(事)< - paste0 (“SAMPLE_ seq_len (ncol(东西)))的事情
# # # #类:ExampleClass暗淡:10 7 # #元数据(0):# #化验(1):计数# # rownames (10): FEATURE_1 FEATURE_2……构成了rowData名字FEATURE_9 FEATURE_10 # #(2):耶额外# # colnames (7): SAMPLE_1 SAMPLE_2……SAMPLE_6 SAMPLE_7 # # colData名称(1):啊# # rowToRowMat有3列# # # # rowToColMat colToColMat有2行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 (colToColMat = rbind (1)),“colToColMat”) expect_error (ExampleClass (rowToColMat = rbind (1)),“rowToColMat”) expect_error (ExampleClass (colToRowMat = rbind (1)),“colToRowMat”)

最后,我们检查强制方法产生一个有效的对象。

se <——(的事情,“SummarizedExperiment”) conv <——(se, ExampleClass) expect_true (validObject (conv))

4.3getter

测试1 d getter方法:

expect_identical(名字(rowVec(的事),rownames(东西))expect_identical (rowVec(东西,withDimnames = FALSE), RV) expect_identical(名字(colVec(的事),colnames(东西))expect_identical (colVec(东西,withDimnames = FALSE),简历)

测试2 d 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(“额外”% % colnames构成了rowData(东西))()

4.4setter

测试1 d setter方法:

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

测试2 d setter方法:

老< - rowToRowMat(事)rowToRowMat(事)< -孩子expect_equivalent (rowToRowMat(的事),孩子)老< - colToColMat(事)colToColMat(事)< - 2 *旧expect_equivalent (colToColMat(的事),2 *旧)老< - rowToColMat(事)rowToColMat(事)< -老+ 1 expect_equivalent (rowToColMat(的事),老+ 1)老< - colToRowMat(事)colToRowMat(事)<岁/ 10 expect_equivalent (colToRowMat(的事),旧/ 10)

测试我们的习俗rowData < -方法:

expect_message构成了rowData(件)(< - 1,“嗨”)

我们确保我们能够成功地引发错误的有效方法:

expect_error (rowVec(事情)< - 0,“rowVec”) expect_error (colVec(事)< - 0,“colVec”) expect_error (rowToRowMat(事情)<——rbind (0),“rowToRowMat”) expect_error (colToColMat(事情)<——rbind (0),“colToColMat”) expect_error (rowToColMat(事情)<——rbind (0),“rowToColMat”) expect_error (colToRowMat(事情)<——rbind (0),“colToRowMat”)

4.5其他修改功能

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

修改< -正常化(事)expect_equal (rowVec(修改),-rowVec(东西))expect_equal (colVec(修改),-colVec(东西))

4.6构造子集的方法

由行构造子集:

subbyrow < -件事[1:5]expect_identical (rowVec (subbyrow) rowVec(事)[1:5])expect_identical (rowToRowMat (subbyrow) rowToRowMat(事)[1:5,])expect_identical (colToRowMat (subbyrow) colToRowMat(事)[1:5])#列影响……expect_identical (colVec (subbyrow) colVec(东西))expect_identical (colToColMat (subbyrow) colToColMat(东西))expect_identical (rowToColMat (subbyrow) rowToColMat(东西))

通过列构造子集:

subbycol < -件事[1:2]expect_identical (colVec (subbycol) colVec(事)[1:2])expect_identical (colToColMat (subbycol) colToColMat(事)[1:2])expect_identical (rowToColMat (subbycol) rowToColMat(事)[1:2,])#行影响……expect_identical (rowVec (subbycol) rowVec(东西))expect_identical (rowToRowMat (subbycol) rowToRowMat(东西))expect_identical (colToRowMat (subbycol) colToRowMat(东西))

检查构造子集创建一个空对象是可能的:

norow < -件事[0]expect_true (validObject (norow)) expect_identical (nrow (norow), 0 l) nocol < -件事[0]expect_true (validObject (nocol)) expect_identical (ncol (nocol), 0 l)

子集的任务:

修改< -修改(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,))

检查后我们获得相同的对象简单赋值操作:

修改< -修改[0]< -东西[0]expect_equal修改(修改件)[1]< -件事[1]expect_equal(修改件)修改[0]< -件事[0]expect_equal修改(修改件)[1]< -件事[1]expect_equal(修改)

我们仔细检查,我们可以得到一个错误无效的任务:

expect_error(修改[1]< -(0,0),更换长度为0)

4.7结合的方法

结合行:

结合< - rbind(东西,东西)rtwice <——代表(seq_len (nrow(的事),2)expect_identical (rowVec(组合),rowVec(事)[rtwice]) expect_identical (rowToRowMat(组合),rowToRowMat(事)[rtwice,]) expect_identical (colToRowMat(组合),colToRowMat(事)[,rtwice]) #列不受影响:expect_identical (colVec(组合),colVec(东西))expect_identical (colToColMat(组合),colToColMat(东西))expect_identical (rowToColMat(组合),rowToColMat(东西))

并结合所列。我们使用test_equivalent为简单起见,在列名修改保持唯一性。

结合< - cbind(东西,东西)ctwice <——代表(seq_len (ncol(的事),2)expect_equivalent (colVec(组合),colVec(事)[ctwice]) expect_equivalent (colToColMat(组合),colToColMat(事)[,ctwice]) expect_equivalent (rowToColMat(组合),rowToColMat(事)[ctwice,]) #行不受影响:expect_equivalent (rowVec(组合),rowVec(东西))expect_equivalent (rowToRowMat(组合),rowToRowMat(东西))expect_equivalent (colToRowMat(组合),colToRowMat(东西))

检查我们得到相同的对象如果我们把一个对象或一个空对象:

expect_equal(东西,rbind(东西))expect_equal(东西,rbind(东西,东西[0]))expect_equal(东西,cbind(东西))expect_equal(东西,cbind(东西,东西[0]))

和检查兼容性错误是正确的:

[expect_error (rbind(东西,东西,ncol(的事):1]),“不兼容”)expect_error (cbind(东西,东西[nrow(的事):1)),“不兼容”)

5文档

我们建议(即至少创建两个单独的文档。* .Rd)文件。第一个文件文档类和构造函数:

\名字{ExampleClass类}\别名{ExampleClass-class} {ExampleClass} \ \别名标题{ExampleClass类}\描述{ExampleClass类的概述和构造函数。}\使用{ExampleClass (rowVec =整数(0),colVec =整数(0),#等等,等等,这里我就不写出来。)}\参数{\项{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. }

第二个文件会记录所有个人的方法:

名字\ {ExampleClass方法}%新的泛型:{rowVec} \ \别名别名{rowVec, ExampleClass-method} {{rowVec < -} \ \别名别名rowVec < - - - - - -, ExampleClass-method} % %等等…%已经有一个通用的:{{[,ExampleClass-method} \ \别名别名(ExampleClass,任何方法}\别名{[ExampleClass,任何,任何方法}{rbind, ExampleClass-method} % % \别名等等……描述\标题{ExampleClass方法}\ {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 # # # #矩阵产品:默认# #布拉斯特区:/home/biocbuild/bbs - 3.13 - bioc / R / lib / libRblas。所以# # LAPACK: /home/biocbuild/bbs - 3.13 - bioc / R / lib / libRlapack。# # # #语言环境:# # [1]LC_CTYPE = en_US。utf - 8 LC_NUMERIC = C # #[3]而= 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 = C LC_TELEPHONE = # # [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.5.1 compiler_4.1.0 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 # # [16] DelayedArray_0.18.0 yaml_2.2.1 xfun_0.23 # # [19] GenomeInfoDbData_1.2.6 withr_2.4.2 stringr_1.4.0 # # [22] knitr_1.33 desc_1.3.0 sass_0.4.0 # # [25] rprojroot_2.0.2 grid_4.1.0 glue_1.4.2 # # [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