健壮而高效的代码

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

指导原则

  1. 主要原则是确保您的代码是正确的。使用相同的()或者All.Equal()以确保正确性和单元测试确保代码修订的一致结果。

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

  3. 知道何时停止尝试效率。如果代码占用一秒钟的分数来评估,则在尝试进一步改进方面没有意义。使用system.time ()或者像这样的包裹Microbenchmark.量化性能收益。

常见的建议

Vectorize

矢量化,而不是迭代(为了循环,lapply(),应用()有常见的迭代成语吗R)。一个调用Y < - SQRT(x)用一个向量x的长度n是一个向量化函数的例子。呼叫,例如y <- sapply(x,√)或者一个为了环形for (i in seq_along(x)) y[i] <- sqrt(x[i])是同一调用的迭代版本,应该避免。通常,可以向量化的迭代计算“隐藏”在a的主体中为了环形

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

并且可以“悬挂”循环

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

通常这个原则可以反复应用,反复迭代为了循环变成了几行向量化的函数调用。

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

Preallocate-and-fill(通常是通过lapply()或者vapp ()),而不是复制并附加。如果创建一个向量或结果列表,请使用lapply()(创建一个列表)或vapp ()(创建vector对象)而不是为了环形。例如,

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

管理内存分配x并且紧凑地代表正在执行的变换。一种为了如果迭代具有副作用(例如,显示绘图)或者一个值的计算取决于先前值,则循环可能是合适的。在创建矢量时为了循环,始终预先分配结果

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

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

not_this < - 函数(n){x < -  integer()for(i在seq_len(n))x [i] = i x}

此模式复制了当前值x每次都通过循环,制作n ^ 2/2总拷贝数,即使是简单的计算,伸缩性也很差:

> system.time(not_this(1000) user system elapsed 0.004 0.000 0.004 > 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: n或者1:长度(x)。这可以防止这种情况n或者长度(x)是0(在实际代码中经常出现意外的“边界情况”)或负数。

重用现有的功能

有关常用输入格式,请参阅常见的生物体方法和类

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

重用现有的类

重用增强了两者之间的互操作性生物体提供数据操作的强大代码的包装。

使用Genomicranges :: Granges.(和GRangesList)代表基于1个,闭合间隔基因组坐标。

使用SummarizedExperiment: SummarizedExperiment(带或不带范围作为行数据)用特征和样本描述来协调矩形特征x样本数据(如RNAseq计数矩阵)。使用概括分析而不是老年人表达式,特别是对序列数据。

有关更多现有课程,请参阅常见的生物体方法和类

基本S4接口

记得重复使用常见的生物体方法和类在实施新的陈述之前。这鼓励互操作性并简化您自己的包装开发。

如果您的数据需要新的表示或函数,请仔细设计一个S4类或泛型,以便具有类似需求的其他包开发人员能够重用您的努力工作,并使相关包的用户能够无缝地使用您的数据结构。bob电竞体育官网不要犹豫,请向bioco -devel邮件列表中询问建议。

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

实现一个表演()方法为了有效地将信息传达给您的用户,而不会使它们更加细节。

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

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

静电:在包装安装期间,可能需要在描述文件中进行描述和方法定义所必需的配置。

并行推荐

我们建议使用BiocParallel。它为用户提供了一致的接口,并支持主要的并行计算风格:单台计算机上的分支和进程、特别集群、批调度程序和云计算。默认情况下,BiocParallel选择适合操作系统的并行后端,并在UNIX,MAC和Windows中支持。编码要求BiocParallel是:

有关更多信息,请参阅生物偏离小插图