内容

原位作者:瓦莱丽·奥根特,马丁摩根
提交作者:马丁摩根(martin.morgan@roswellpark.org.)日期:2016年6月25日

高效的R代码

本节的目标是强调编写正确、健壮和高效的R代码的实践。

优先级

  1. 正确:与手工实施例一致(完全相同的()All.Equal()
  2. 强大:支持现实输入,例如0长向量,NA.价值观,......
  3. 简单:易于理解下个月;容易描述它对同事做了什么;易于发现逻辑错误;易于增强。
  4. 鉴于现代计算机的速度,快速,或至少合理。

策略

  1. 轮廓
  1. 矢量化 - 在向量上运行,而不是明确的循环

    x < -  1:10 log(x)##不是for(i在seq_along中)x [i] < -  log(x [i])
    ## [1] 0.0000000 0.6931472 1.0986123 1.3862944 1.6094379 1.7917595 1.9459101 ## [8] 2.0794415 2.1972246 2.3025851
  2. 预先分配内存,然后填写结果

    结果< -  numeric(10)结果[1] < -  runif(1)for(i在2:length(结果))结果[i] < -  runif(1)*结果[i-1]结果
    ## [6] 1.391109e-03 1.085354e-04 8.256891e-05 6.167133e-05 4.673066e-05
  3. 在重复计算之外的升值常见子表达式,使子表达式仅计算一次
  1. 重新使用现有,测试代码

回到顶部

案例研究:预分配和矢量化

这是一个明显低效的功能:

f0 < - 函数(n,a = 2){## stopfnot(是.integer(n)&&(长度(n)== 1)&& ##!是.na(n)&&(n> 0))结果< -  numeric()for(i在seq_len(n)中)结果[[i]] < -  a * log(i)结果}

system.time ()调查该算法如何衡量n,专注于经过的时间。

system.time (f0 (10000))
##用户系统经过## 0.139 0.000 0.139
n < -  1000 * seq(1,20,2)t < -  sapply(n,function(i)system.time(f0(i))[[3]]图(t〜n,type =“b”)

记住当前的“正确”值,以及一个近似的时间

N <- 10000系统。预计时间(< - f0 (n))
##用户系统运行## 0.155 0.000 0.156
头(预期)
## [1] 0.000000 1.386294 2.197225 2.772589 3.218876 3.583519

修改呼吸常见乘法器的功能,一个, 跳出循环。确保“优化”和原始计算的结果相同。使用Microbenchmark.包以比较两个版本

F1 <- function(n, a=2) {result <- numeric() for (i in seq_len(n)) result[[i]] <- log(i) a * result} identical(expected, F1 (n))
# # [1]
库(MicroBenchmark)Microbenchmark(F0(n),f1(n),times = 5)
##单位:毫秒为:毫秒敏捷LQ均值UQ MAX NEVAL ## F0(n)118.6033 123.8989 132.8397177.4337 5

采用“预先分配和填充”策略

F2 <- function(n, a=2) {result <- numeric(n) for (i in seq_len(n)) result[[i]] <- log(i) a * result} identical(expected, F2 (n))
# # [1]
Microbenchmark(F0(N),F2(N),时间= 5)
##单位:毫秒## EXPR分钟LQ平均中值最大UQ ## neval F0(n)的136.82808 180.08703 172.27453 181.08084 181.26852 182.1082 5 ## F2(n)的18.75233 18.85918 20.29981 20.28972 20.37353 23.2243 5

使用一个*应用()避免必须明确地预先分配的功能,并使矢量化更加明显。

F3 < - 函数(n,a = 2)a * sapply(seq_len(n),log)相同(预期,f3(n))
# # [1]
Microbenchmark(F0(N),F2(N),F3(N),时间= 10)
##单位:毫秒## EXPR分钟LQ平均中值最大UQ ## F0(n)的134.451852 137.003841 171.20652 183.653188 188.31813 191.75448 ## F2(n)的18.745654 18.833771 20.50144 20.254524 20.88116 23.44838 ## F3(n)的9.467853 9.651863 10.05424 9.729817 10.56676 11.24802## Neval ## 10#10 ## 10

现在代码显示在一行中,很明显它可以很容易地向量化。抓住机会将其矢量化:

F4 < - 函数(n,a = 2)a * log(seq_len(n))相同(预期,f4(n))
# # [1]
Microbenchmark(F0(n),f3(n),f4(n),时间= 10)
##单位:微秒## EXPR分钟LQ平均中值最大UQ ## F0(n)的135787.889 137468.593 170961.713 184137.111 187689.288 188119.542 ## F3(n)的8705.995 9402.749 9507.574 9554.713 9774.741 9841.998 ## F4(N)517.837 522.898 524.170 525.450 526.068 526.819## Neval ## 10#10 ## 10

f4 ()绝对似乎是胜利者。它如何扩展n?(重复几次)

n < -  10 ^(5:8)#100x大于f0 t < -  sapply(n,function(i)system.time(f4(i))[[3]])绘图(t〜n,log =“xy“,type =”b“)

对不同响应模式的解释?

得到教训:

  1. 向量化在迭代中提供了巨大的改进
  2. 当需要显式迭代时,预分配和填充非常有用。
  3. *应用()功能有助于避免需要明确的预分配,并为矢量化的机会更加明显。这可能会出现小的性能成本,但通常是值得的
  4. 当需要显式迭代时,提升公共子表达式有助于提高性能。

回到顶部

管理内存的迭代和限制

当数据太大而无法装入内存时,我们可以以块的形式遍历文件,或者按字段或基因组位置来划分数据子集。

迭代 - 块明智 -打开(),阅读块(s),关闭().——例如,屈服争论RSAMTOOLS :: BAMFILE()- 框架:GenomicFiles :: DreambyByield()

限制—对感兴趣的列和/或行进行限制—利用特定于领域的格式—BAM文件和Rsamtools: ScanBamParam ()- BAM文件和RSAMTOOLS :: PILPPALAM()- VCF文件和VariantAnnotation :: ScanVCFParam()-使用数据库

练习:计算重叠

迭代文件:`genomicfiles :: dreambybyyield()

  1. 产生一大块
  2. 从输入块映射到可能转换的表示
  3. 减少映射块
suppressPackageStartUpMessages({库(基因组)库(基因组)库(RSAMTools)库(TXDB.hsapiens.ucsc.hg19.knowngene)})产量< - #如何输入数据函数的下一个块(x,...){readgalignments(x)} map < - #到每个块函数(value,...,roi){olaps < -  sopeoverlaps(value,roi,type =“内部”,ignore.strand = true)count < -Tabulate(主题(OLAPS),主管长度(OLAP))符合姓(计数,名称(ROI))}减少<--` +`#如何组合映射的块

改进:“产量厂”跟踪输入有多少录

exitingFactory < - #返回具有本地状态函数(){n_records < -  0l函数(x,...){aln < -  readgalignments(x)n_records <<  -  n_records + length(aln)消息(n_records)aln}}

感兴趣的区域,像bam文件中的染色体一样命名。

Exbytx < -  Exonsby(txdb.hsapiens.ucsc.hg19.knowngene,“tx”)fl < - “/ home/ubuntu/data/vobencha/largedata/srarchive/hg19_alias.tab”map0 < -  read.delim(fl,header= false,stringsasfactors = false)seqlevels(exbytx,force = true)< -  setNames(map0 $ v1,map0 $ v2)

一个遍历bam文件的函数。

count1 < - 函数(filename,roi){message(文件名)##创建和打开BAM文件BF < -  BAMFILE(FILENAME,ExuctionSize = 1000000)DreambyByield(BF,EucketFactory(),Map,Defile,ROI = ROI)}

在行动中

BAM < - “/home/ubuntu/data/vobencha/largedata/srarchive/srr1039508_sorted.bam”count < -  count1(bam,exbytx)

回到顶部

文件管理

文件类

类型 示例使用 的名字 包裹
。床 范围注释 Bedfile() rtracklayer.
。假发 覆盖范围 wigfile()bigwigfile() rtracklayer.
.gtf 记录模型 gtffile() rtracklayer.
makeTxDbFromGFF () GenomicFeatures
.2bit 基因组序列 twobitfile() rtracklayer.
.fastq 读取和品质 fastqfile() ShortRead
.BAM. 对齐的阅读 BamFile () Rsamtools
.tbx. 索引标签分隔 tabixfile() Rsamtools
.vcf 变体呼叫 VcfFile () VariantAnnotation.
## rtracklayer menagerie suppresspackagestartupmessages(库(rtracklayer))名称(getClass(“rtlfile”)@子类)
## [1] "UCSCFile" "GFFFile" "BEDFile" ## [4] "WIGFile" "BigWigFile" "ChainFile" ## [7] "TwoBitFile" "FastaFile" TabSeparatedFile" ## [10] "CompressedFile" "GFF1File" "GFF2File" ## [13] "GFF3File" "BEDGraphFile" "BED15File" ## [19] "GTFFile" "GVFFile" "GZFile" ## [22] "BGZFile" "BZ2File" "XZFile"

笔记

管理文件集合

* filelist()班级

GenomicFiles ()

vcfstack()

回到顶部

平行的评价

几个警告 -

迭代/限制性技术在控制下保持内存要求,而并行评估在节点上分配计算负载。请记住,并行计算仍然受到每个节点上可用的内存量的限制。

在分布式存储器中计算时,在群集中设置和撕毁群集时,有开销。对于小的计算,并行开销可能超过性能没有提高的好处。

从并行执行中获益最多的作业是cpu密集型的,操作的数据块适合内存。

BiocParallel

BiocParallel提供标准化接口,用于并行评估,并支持主要并行计算样式:叉子和过程在单个计算机上,ad hoc集群,批量调度程序和云计算。默认,BiocParallel选择适合操作系统的并行后端,并在UNIX,MAC和Windows中支持。

一般想法:

锻炼:连续和平行睡眠

这个小示例激发了并行执行的使用,并演示了如何使用bplapply ()可以是一下Lapply.

system.time ()探索这需要多长时间执行n从1到10增加完全相同的()Microbenchmark.比较替代方案F0()F1()为了正确性和性能。

乐趣睡1秒钟,然后返回

图书馆(Biocparallel)有趣< - 函数(i){sys.sleep(1)i} ##串行f0 < - 函数(n)lapply(seq_len(n),fun)##并行f1 < -  function(n)bppppply(SEQ_LEN(N),乐趣)

回到顶部

练习:错误处理和BPREDO

BiocParallel“捕获并返回”错误以及成功的结果。本练习演示了如何访问回溯()失败的任务以及如何使用“bpredo”重新运行失败的任务。有关错误处理,日志记录和调试的完整详细信息错误,日志和调试小插图。

param <- MulticoreParam(workers = 3)

打电话给sqrt ()在“X”功能;第二个元素是一个字符,将抛出和错误。

X <- list(1, "2", 3) res <- bplapply(X, sqrt, BPPARAM = param)
##错误:生物竞争错误##元素索引:2 ##第一个错误:数学函数的非数字参数

还可以捕获错误和部分计算的结果

RES < -  BPTRY(BPLAPPLY(X,SQRT,BPPARAM = PARAM))RES
## [[1]] ## [1] 1 ## ## [[2]] ##  ## traceback()作为'attr(x,“回溯”)'## ## [[3]] ## [1] 1.732051

通过重复呼叫重新运行失败的结果bplapply ()这次通过校正的输入数据和部分结果为“BPREDO”。只重新运行失败的值。

x.redo < -  list(1,2,3)Bpprapply(x.redo,sqrt,bpredo = res)
##恢复以前的计算......
## [[1]] ## [1] 1 ## ## [[2]] ## [1] 1.414214 ### [[3]] ## [1] 1.732051

或者,切换到aSerialParam()并调试导致错误的特定元素。

> Fun =函数(i){browser();sqrt(i)}> bpppply(x,fun,bpredo = res,bpparam = serialparam())恢复以前的计算...从:fun(...)浏览[1]>调试#1:sqrt(i)浏览[2]> i [1]“2”浏览[2]> i = 2浏览[2]> c [1]] [1] 1 [[2]] [1] 1.414214 [[3]][1] 1.732051

回到顶部

练习:日志记录

BiocParallel使用futile.logger包的日志记录。该包有一个灵活的系统,用于过滤不同严重性阈值的消息,如INFO、DEBUG、ERROR等(所有阈值的列表请参阅?bpthreshold手册页)。BiocParallel捕获以fution.logger格式编写的消息以及写入stdout和stderr的消息。

这个函数进行一些参数检查,并具有DEBUG、WARN和info级别的日志消息。

有趣< - 函数(i){flog.debug(Paste0(“I'的值:”,i))如果(!长度(i)){flog.warn(“'我缺少”)na}别的如果(!是(i,'numeric“)){flog.info(”强制到数字“)as.numeric(i)} else {i}}

在参数中打开登录,并将阈值设置为WARN。

param < -  snowparam(3,log = true,throupold =“warn”)bplapply(列表(1,“2”,Integer()),Fun,BPParam = Param)

降低信息和调试的阈值(即,使用bpthreshold < -)查看如何在严重性上过滤消息。

回到顶部

练习:工人超时

对于长时间运行的作业或未经测试的代码,可以设置时间限制很有用。的超时字段是每个工人完成任务的时间,以秒为单位,以秒为单位允许。如果任务需要更长的时间超时工作者返回错误。

超时可以在参数施工期间设置,

Param < -  SnowParam(Timeout = 20)Param
## class: SnowParam ## bpisup: FALSE;bpnworkers: 2;bptasks: 0;bpjobname: BPJOB ## bplog: FALSE;bpthreshold:信息;bpstopOnError: TRUE ## bptimeout: 20;bpprogressbar: FALSE ## bpRNGseed: ## bplogdir: NA ## bpresultdir: NA ## cluster type: SOCK

或者与setter:

Bptimeout (param) <- 2参数
## class: SnowParam ## bpisup: FALSE;bpnworkers: 2;bptasks: 0;bpjobname: BPJOB ## bplog: FALSE;bpthreshold:信息;BPStoponError:True ## Bptimout:2;bpprogressbar: FALSE ## bpRNGseed: ## bplogdir: NA ## bpresultdir: NA ## cluster type: SOCK

使用此函数在“x”值的数字向量上探索不同的_timeout_sbplapply ().'x'值小于超时那些人成功返回临界点返回一个错误。

fun <- function(i) {Sys.sleep(i) i}

回到顶部

练习:单独计数重叠

分发文件超过工人:Genomicfiles :: DreambyFile()

使用之前的计数示例GenomicFiles :: DreambyByield()它对单个文件进行操作,并实现了yield、map、reduce范式。在这个练习中,我们将使用Genomicfiles :: DreambyFile()它使用bplaply()在引擎盖下,并行地在多个文件上运行。

主要论点reduceByFile ()是一组文件和一组范围。文件被发送到工作者,并根据范围提取数据子集。大部分工作是在办公室里完成的地图功能和可选减少函数组合每个worker的输出。

suppressPackageStartUpMessages({图书馆(生物竞争)库(GenomicFiles)库(基因组)库(RsamTools)})

在UNIX或Mac上,配置aMulticoreParam()有4名工人。打开日志记录并设置60秒的超时。

param <- MulticoreParam(4, log = TRUE, timeout = 60)

在Windows上做同样的事情SnowParam()

Param < -  SnowParam(4,log = true,超时= 60)

指向BAM文件的集合。

< /home/ubuntu/data/vobencha/LargeData/copynumber < /home/ubuntu/data/vobencha/LargeData/copynumber >bam$", full=TRUE) names(fls) <- basename(fls) bfl <- BamFileList(fls)

定义范围(兴趣区域)限制了工人上的数据量,并在控制下保留内存要求。我们将在染色体6上使用一组范围在主要的组织相容性复合物轨迹上。

范围< -  Granges(“Chr6”,绞油(C(28477797,29527797,32448354),C(29477797,30527797,33448354)))

地图功能在记录中读取并计数重叠。ReadGalignments()对象中定义的范围的任何部分重叠的bam记录Scanbamparam.(即,它们可以重叠开始或结束)。一旦我们有记录R,我们希望只计算那些落在“范围内”的人。

地图< - 函数(范围,文件,......){库(基因组)## ReadGalignments(),ScanBamparam()Param = ScanBamparam(哪个=范围)##限制Gal = Readgalignments(文件,Param = Param)##日志消息flog.info(Paste0(“文件:”,baseName(文件)))flog.debug(Paste0(“记录:”,长度(gal)))##重叠OLAP < -  footoverlaps(GAL,Range,Type =“在”中,icionore.strand = true)Tabulate(主题(OLAPS),主管长度(OLAP))}

数数 …

CTS < -  DreambyByFile(范围,杂志,地图,BPParam = Param)

结果是列出与文件数相同的长度。

长度(CTS)

每个列表元素都是范围的长度。

ElementLengths(CTS)

每个范围的计数表用'[['[[':

cts ([1])

回到顶部

其他资源

回到顶部

资源

回到顶部