内容

作者:Valerie Obenchain(vobencha@fredhutch.org.)、马丁·摩根(mtmorgan@fredhutch.org.
日期:2015年7月22日

高效的R.代码

本节的目标是突出显示正确,强大,高效的R代码的实践。

优先事项

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

策略

  1. 配置文件
  1. 矢量化 - 在向量上运行,而不是明确的循环

    x <- 1 log(x) ## NOT for (i in 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. 预先分配内存,然后填写结果

    Result [i] <- runif(1): Result [i] <- runif(1) * Result [i - 1] Result
    ## [1] 0.610156070 0.326579479 0.319540555 0.182668253 0.078093233 ## [6] 0.056413616 0.021594612 0.017351821 0.006025564 0.004570092
  3. 在重复计算之外的升值常见子表达式,使子表达式仅计算一次
  1. 重用现有的、经过测试的代码

回到顶部

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

这是一个明显低效的函数:

f0 < -函数(n = 2) {# # stopifnot (is.integer (n) & &(长度(n) = = 1) & & # # ! is.na (n) & & (n > 0))结果< -数字()(我在seq_len (n))结果[[我]]< - *日志(i)结果}

使用系统时间()调查该算法如何衡量N,专注于经过的时间。

System.time(F0(10000))
##用户系统运行## 0.150 0.001 0.150
n < -  1000 * seq(1,20,2)t < -  sapply(n,function(i)system.time(f0(i))[[3]]图(t〜n,type =“b”)

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

n < -  10000 system.time(预期< -  f0(n))
##用户系统经过## 0.141 0.000 0.142
(预计)
## [1] 0.000000 1.386294 2.197225 2.772589 3.218876 3.583519

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

f1 < - 函数(n,a = 2){for(i在seq_len(n)中)的结果< -  numeric()结果[[i]] < -  log(i)a *结果}相同(预期,f1(n)))
## [1]真实
库(MicroBenchmark)Microbenchmark(F0(n),f1(n),times = 5)
##单位:毫秒## EXPR分钟LQ平均中值最大UQ ## neval F0(N)98.11787 154.91307 145.9875 158.2557 158.9396 159.7114 5 ## F1(n)的96.93034 97.99369 133.8523 156.3321 158.9998 159.0055 5

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

F2 < - 函数(n,a = 2){结果< -  numeric(n)for(i在seq_len(n)中)结果[[i]] < -  log(i)a *结果}相同(预期,f2(n))
## [1]真实
微基准测试(f0 (n), f2 (n),时间= 5)
##单位:毫秒:毫秒LQ均值UQ MAX NEVAL ## F0(n)102.20437 162.75624 152.84236 163.095 167.9829 168.21692 5#05/21692 5 #05322(n)11.0532211335959 13.7177611/121135959 13.0532211335959 13.717761133293581C1111177613/13.05322

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

F3 < - 函数(n,a = 2)a * sapply(seq_len(n),log)相同(预期,f3(n))
## [1]真实
微基准测试(f0(n), f2(n), f3(n), times=10)
##单位:毫秒## EXPR分钟LQ平均中值最大UQ ## F0(n)的100.830765 107.964489 145.823291 160.782134 164.901310 166.962340 ## F2(n)的9.370014 11.162675 12.739058 11.709209 15.512331 17.017115 ## F3(n)的4.640934 4.703644 5.799298 5.444969 6.617305 8.305891## Neval ## 10#10 ## 10

既然代码以单行呈现,那么很明显它可以很容易地矢量化。抓住机会向量化它:

F4 <- function(n, a=2) a * log(seq_len(n)) identical(expected, F4 (n))
## [1]真实
Microbenchmark (f0(n), f3(n), f4(n), times=10)
##单位:微秒## EXPR分钟LQ平均中值最大UQ ## F0(n)的101977.842 158804.803 150045.0529 161249.9995 162876.991 165036.864 ## F3(n)的4221.855 4419.104 4925.7432 4651.7185 4885.559 6511.562 ## F4(N)396.465 401.305 412.4188 411.3425 418.137 441.464## 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. 当需要显式迭代时,提升普通子表达可以有助于提高性能。

回到顶部

管理内存的迭代和限制

当数据太大而无法适合内存时,我们可以通过块中的文件迭代文件,或者通过字段或基因组位置子集。

迭代-块明智-打开(),阅读块,close ()。- 例如,yieldSize争论RSAMTOOLS :: BAMFILE()-框架:GenomicFiles :: DreambyByield()

限制 - 限制列和/或兴趣的行 - 利用域特定的格式 - BAM文件和RSAMTOOLS :: SCANBAMPARAM()- BAM文件和Rsamtools: PileupParam ()- VCF文件和VariantAnnotation :: scanvcfparam()- 使用数据库

练习:计算重叠

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

  1. 产生一大块
  2. 从输入块映射到可能转换的表示
  3. 减少映射的块
suppressPackageStartupMessages({库(GenomicFiles)图书馆(GenomicAlignments)图书馆(Rsamtools)库(TxDb.Hsapiens.UCSC.hg19.knownGene)})收益率< - #如何输入的下一块数据函数(X,…){readGAlignments (X)} < - #如何映射到每个块功能(价值,…{olaps <- findoverlap (VALUE, roi, type="within", ignore.strand=TRUE) count <- tabulate(subjectHits(olaps), subjectLength(olaps)) setNames(count, names(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 <- function(filename, roi) {message(filename) ##创建并打开BAM文件bf <- BamFile(filename, yieldSize=1000000) reduceByYield(bf, yieldFactory(), map, reduce, roi=roi)

在行动

bam <——“/ home / ubuntu /数据/ vobencha LargeData / srarchive / SRR1039508_sorted。count <- count1(bam, exByTx)

回到顶部

文件管理

文件类

类型 示例使用 名称
请全部 范围注释 Bedfile() rtracklayer
。假发 覆盖范围 WigFile ()BigWigFile () rtracklayer
.GTF. 转录模型 gtffile() rtracklayer
maketxdbfromgff() 基因组法
.2bit. 基因组序列 TwoBitFile () rtracklayer
.fastq. 读取和品质 FastqFile () 缩短
本演讲 对齐的阅读 Bamfile() RSAMTOOLS.
.tbx. 索引标签分隔 tabixfile() RSAMTOOLS.
.vcf. 变体呼叫 vcffile() VariantAnnotation.
## rtracklayer menagerie suppresspackagestartupmessages(库(rtracklayer))名称(getClass(“rtlfile”)@子类)
## [1]“ucscfile”“gfffile”“bedfile”## [4]“wigfile”“bigwigfile”“chabfile”## [7]“twobitfile”“fastafile”“tabseparatedfile”## [10]“compressurefile”“gff2file”“gff2file”## [13]“gff3file”“bedgraphfile”“bed15file”## [16]“bwfile”“2bitfile”“gtffile”## [19]“gvffile”“gzfile”“bgzfile”##[22]“bz2file”“xzfile”

笔记

管理文件集合

*文件列表()班级

GenomicFiles()

回到顶部

并行评估

几个警告 -

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

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

受益于并行执行中最大的作业是CPU密集型,并在适合内存的数据块上运行。

生物相投

生物相投为并行计算提供标准化接口,并支持主要的并行计算风格:在单个计算机上的分叉和进程、AD hoc集群、批调度程序和云计算。默认情况下,生物相投选择适合操作系统的并行后端,并在UNIX,MAC和Windows中支持。

一般想法:

练习:串行睡觉

这个小示例激励了并行执行的使用,并演示了如何bpppply()可以是一下拉普兰人

使用系统时间()以了解执行此操作所需的时间N从1增加到10。使用相同的()Microbenchmark.比较替代方案f0 ()F1()对于正确性和性能。

有趣的睡眠1秒,然后返回一世

# f0 <- function(n) lapply(seq_len(n), fun) ## parallel f1 <- function(n) bplapply(n), fun)

回到顶部

练习:错误处理和bpredo()

生物相投“捕获并返回”错误以及成功的结果。本练习演示了如何访问追溯()如何使用' BPREDO '重新运行失败的任务。文档中提供了错误处理、日志记录和调试的详细信息错误、日志和调试装饰图案。

- MulticoreParam(workers = 3

打电话给sqrt()跨越'x';第二个元素是一个角色,并将抛出和错误。

X <- list(1, "2", 3) res <- bplapply(X, sqrt, BPPARAM = param) res

与错误消息一起,回溯存储在返回列表元素中,并可以访问attr()

attr(res [[2]],“回溯”)

通过重复呼叫重新运行失败的结果bpppply()这次使用的是正确的输入数据和部分结果为' BPREDO '。

x.redo < -  list(1,2,3)Bpprapply(x.redo,sqrt,bpredo = res)

回到顶部

锻炼:伐木

生物相投用来徒劳。霍尔格用于记录的包。该软件包具有灵活的系统,用于过滤不同的严重性阈值的消息,例如信息,调试,错误等(对于所有阈值的列表,请参阅?BPThreshold手册页。)生物相投捕获以fution.logger格式编写的消息以及写入stdout和stderr的消息。

此功能有一些参数检查并有调试,警告和信息级日志消息。

FUN <- function(i) {flog.debug(paste0("value of 'i': ", i)) if (!NA} else if (!)一世s(i, "numeric")) { flog.info("coercing to numeric") as.numeric(i) } else { i } }

在参数中打开登录并设置阈值以警告。

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

将阈值降低到INFO和DEBUG(即,使用bpthreshold < -)查看如何在严重性上过滤消息。

回到顶部

练习:工人超时

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

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

SnowParam <- SnowParam(timeout = 20
##类:SnowParam ## BPJobname:BPJOB;BPWorkers:2;BPTASKS:0;BPTIMOUT:20;bprgseed :;BPISUP:FALSE ## BPLOG:FALSE;BPThreshold:信息;bplogdir:na ## bpstoponerror:false;bpprogressbar:false ## bpresultdir:na ##群集类型:袜子

或者与setter:

bptimout(param)< -  2 param
##类:SnowParam ## BPJobname:BPJOB;BPWorkers:2;BPTASKS:0;bptimout:2;bprgseed :;BPISUP:FALSE ## BPLOG:FALSE;BPThreshold:信息;bplogdir:na ## bpstoponerror:false;bpprogressbar:false ## bpresultdir:na ##群集类型:袜子

使用此函数可以使用“X”值的数值向量探索不同的_timeout_sbpppply()。'x'值小于超时那些人成功返回阈值返回错误。

有趣< - 函数(i){sys.sleep(i)i}

回到顶部

练习:单独计数重叠

在工人中分发文件:Genomicfiles :: DreambyFile()

前面使用的计数示例GenomicFiles :: DreambyByield()在单个文件上运行并实现产量,映射,减少范例。在这项运动中,我们将使用Genomicfiles :: DreambyFile()哪种用途bplaply()在引擎盖下,并行地在多个文件上运行。

主要论点dreambybyfile()是一组文件和一组范围。文件已发送到工人,并且基于范围提取数据子集。这项工作的大部分是在的地图函数和一个可选的减少函数将输出组合在每个工作者上。

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

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

Param < -  MulticoreParam(4,log = true,timeout = 60)

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

(4, log = TRUE, timeout = 60)

指向BAM文件的集合。

FLS < -  dir(“/ home / ubuntu / data / data / largedata / copynumber”,“.bam $”,full = true)名称(fls)< -  baseName(vs)bfl < -  bamfilelist(fls)

定义范围(感兴趣的区域)限制worker上的数据量,并保持对内存需求的控制。我们将使用6号染色体上主要组织相容性复合体位点的一组范围。

range <- GRanges("chr6", IRanges(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]]

回到顶部

其他资源

回到顶部

资源

回到顶部