健壮高效的代码

R可以是一种健壮、快速和高效的编程语言,但一些编码实践可能非常不幸。这里有一些建议。

指导原则

  1. 最主要的原则是确保你的代码是正确的。使用相同的()all.equal ()为了确保正确性,以及单元测试确保代码修订的结果一致。

  2. 编写健壮的代码。避免不容易处理边缘情况的效率,例如0长度或NA值。

  3. 知道什么时候停止试图提高效率。如果计算代码只需要几分之一秒的时间,那么尝试进一步改进就没有意义了。使用system.time ()或者像这样的包裹微基准测试量化绩效收益。

常见的建议

Vectorize

向量化,而不是迭代(循环,拉普兰人(),应用()是否存在常见的迭代习惯用法R)。一个调用y < - sqrt (x)用一个向量x的长度n是向量化函数的一个例子。像这样的呼叫Y <- sapply(x, sqrt)或者一个循环For (i in seq_along(x)) y[i] <- sqrt(x[i])是同一调用的迭代版本,应该避免。通常,可以向量化的迭代计算“隐藏”在循环

For (i in seq_len(n)){##…TMP <- foo(x[i]) y <- TMP + ##…}

可以被“吊”出环

TMP <- foo(x) for (i in seq_len(n)){##…Y <- tmp[i] + ##}

通常情况下,这个原则可以重复应用,并且是迭代的循环变成了几行向量化函数调用。

“预分配和填充”如果迭代是必要的

Preallocate-and-fill(通常是通过拉普兰人()vapp ()),而不是复制和附加。如果创建一个向量或结果列表,使用拉普兰人()(创建列表)或vapp ()(创建一个向量)而不是循环。例如,

n < - 10000 x < vapp (seq_len (n),(我的函数 ) { ## ...},整数(1))

的内存分配x紧表示正在进行的变换。一个如果迭代有副作用(例如,显示一个图),或者一个值的计算依赖于之前的值,那么循环可能是合适的。控件中创建向量时循环,总是预分配结果

x < -整数(n)如果(n > 0) x[1] < - 0(我在seq_len (n - 1)) {# # * (i + 1) < -…}

从来没有采用“复制-附加”策略

no_this <- function(n) {x <- integer() for (i in seq_len(n)) x[i] = i x}

的当前值x每一次通过循环,使n ^ 2 / 2总拷贝数,即使是在琐碎的计算中伸缩性也很差:

> system.time(not_this(10000)) user system elapsed 0.169 0.000 0.168 > system.time(not_this(100000)) user system elapsed 22.827 1.120 23.936

避免1: n风格的迭代

seq_len (n)seq_along (x)而不是1: n1:长度(x)。这样可以防止n长度(x)为0(在实际代码中经常作为意外的“边缘情况”出现)或为负。

重用现有的功能

有关常见的输入格式,请参阅常见的Bioconductor方法和类

如果出现问题,例如性能问题或解析特定文件类型时,请向bioc-devel邮件列表中的其他开发人员询问输入。2021欧洲杯体育投注开户“实现你自己的”的常见缺点是引入了非标准数据表示(例如,忽略了将文件格式的坐标系统转换为Bioconductor对象)和用户困惑。

重用现有的类

重用增强了之间的互操作性Bioconductor包,同时为数据操作提供健壮的代码。

使用GenomicRanges:农庄(和GRangesList)表示以1为基础的封闭区间基因组坐标。

使用SummarizedExperiment: SummarizedExperiment(有或没有范围作为行数据)坐标矩形特征x样本数据(例如,RNAseq计数矩阵)与特征和样本描述。使用SummarizedExperiment而不是老的ExpressionSet,特别是序列数据。

有关更多现有类,请参阅常见的Bioconductor方法和类

基本S4接口

记得重用常见的Bioconductor方法和类在实现新的表示之前。这鼓励了互操作性并简化了您自己的包开发。

如果您的数据需要新的表示或函数,请仔细设计一个S4类或通用类,以便具有类似需求的其他包开发人员能够重用您的辛苦工作,并使相关包的用户能够无缝地使用您的数据结构。2021欧洲杯体育投注开户不要犹豫,请在Bioc-devel邮件列表上询问建议。

对于您定义的任何类,实现并使用一个“构造函数”来创建对象。构造函数通常是普通的旧函数(而不是具有方法的泛型)。它提供了文档化和用户友好的参数,同时允许开发人员友好的实现。在您自己的代码、示例和小插图中使用构造函数。

实现一个显示()有效地向用户传达信息的方法,而不是用细节压倒他们。

访问器(返回对象组件的简单函数)而不是直接的槽访问(使用@)有助于将类的实现与其接口隔离开来。一般@应该只在访问器中使用,所有其他代码都应该使用该访问器。如果用户不需要或业务访问数据对象的部分,则不需要从类导出访问器。普通的旧函数(而不是泛型的+方法)通常对访问器足够了;通常使用(一致地)轻量级名称混淆方案(例如,用2或3个字母的首字母缩写开始您的包的访问方法名)以避免其他包中名称相似的函数之间的名称冲突是很有用的。

下面的布局有时用于组织类和方法;其他的方法是可能的和可接受的。

DESCRIPTION文件中的Collates:字段可能需要在包安装期间对类和方法定义进行适当的排序。

平行的建议

我们建议使用BiocParallel。它为用户提供了一致的界面,并支持主要的并行计算风格:单个计算机上的分叉和进程、特设集群、批调度程序和云计算。默认情况下,BiocParallel选择适合操作系统的并行后端,并且在Unix、Mac和Windows上都得到支持。编码的要求BiocParallel是:

有关更多信息,请参见BiocParallel装饰图案