原作者:马丁·摩根,索纳利·阿罗拉
展示作者:马丁•摩根
时间:2019年7月26日
本节的目的是强调编写正确、可理解、健壮和高效的R代码的实践。
相同的()
,all.equal ()
)NA
值,…配置文件
向量化——操作向量,而不是显式循环
x <- 1:10 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
预分配内存,然后填充结果
Result <- numeric(10) Result [1] <- runif(1) for (i in 2:length(Result)) Result [i] <- runif(1) * Result [i - 1] Result
## [1] 0.8909704231 0.4146406038 0.0571932985 0.0284667752 0.0070462630 ## [6] 0.0061630984 0.0024937168 0.0022745184 0.0003264045 0.0001861150
将公共子表达式提升到重复计算之外,以便子表达式只计算一次
为
循环lm.fit ()
而不是重复拟合相同的设计矩阵。重用现有的、测试过的代码
汇总()
,rowSums ()
和朋友,%, %
,……这是一个低效率的函数:
F0 <- function(n, a=2) {## stopifnot(is.integer(n) && (length(n) == 1) && ## !is.na(n) && (n > 0)) result <- numeric() for (i in seq_len(n)) result <- c(result, a * log(i)) result}
使用system.time ()
研究这个算法是如何扩展的n
,主要关注运行时间。
system.time (f0 (10000))
##用户系统运行## 0.142 0.044 0.187
n < - 1000 * seq(2) 1, 20日t < -酸式焦磷酸钠(n,函数(i) system.time (f0(我)[[3]])情节(t ~ n、类型=“b”)
记住当前的“正确”值和一个近似的时间
N <- 10000系统。时间(预期<- f0(n))
##用户系统运行## 0.130 0.027 0.158
(预计)
## [1] 0.000000 1.386294 2.197225 2.772589 3.218876 3.583519
修改提升常用乘法器的功能,一个
,在圈子之外。确保“优化”的结果和原来的计算是相同的。使用微基准测试包来比较两个版本
F1 <- function(n, a=2) {result <- numeric() for (i in seq_len(n)) result <- c(result, log(i)) a * result}相同(预期,F1 (n))
##[1]正确
微基准测试(f0(n), f1(n), times=5)
##单位:毫秒## expr min lq mean median uq max neval cld ## f0(n) 106.0858 106.8267 110.0829 108.6577 111.2711 117.5732 5 a ## f1(n) 106.5467 107.5789 109.9853 109.8549 111.0009 114.9453 5 a
采用“预分配和填充”策略
F2 <- function(n, a=2) {result <- numeric(n) for (i in seq_len(n)) result[[i]] <- log(i) a * result}相同(预期,F2 (n))
##[1]正确
微基准测试(f0(n), f2(n), times=5)
##单位:microseconds ## expr min lq mean median uq max ## f0(n) 125407.885 128915.301 158967.1082 156380.107 158970.766 225161.482 ## f2(n) 522.673 578.257 646.8576 678.658 689.675 765.025 ## neval cld ## 5 b# # 5 a
使用一个*应用()
函数,以避免必须显式预分配,并使向量化的机会更明显。
F3 <- function(n, a=2) a * sapply(seq_len(n), log)相同(预期,F3 (n))
##[1]正确
微基准测试(f0(n), f2(n), f3(n),乘以=10)
##单位:microseconds ## expr min lq mean median uq max ## f0(n) 116236.001 117543.999 125837.8697 122340.567 128809.586 158061.487 ## f2(n) 535.368 549.919 582.1863 571.667 594.484 688.245 ## f3(n) 2371.288 2467.712 2773.2181 2655.956 2827.480 4202.900 ## neval cld ## 10 b# # 10 a
既然代码是在单行中显示的,显然可以很容易地向量化它。抓住机会向量化它:
F4 <- function(n, a=2) a * log(seq_len(n))相同(期望,F4 (n))
##[1]正确
微基准(f0(n), f3(n), f4(n), times=10)
##单位:microseconds ## expr min lq mean median uq max ## f0(n) 108984.448 112646.525 128658.2867 117566.5805 122747.206 224810.810 ## f3(n) 2319.026 2427.799 2583.1335 2526.5675 2582.082 3334.533 ## f4(n) 61.674 64.410 202.8533 65.2965 70.140 1430.968 ## neval cld ## 10 b# # 10 a
f4 ()
看起来绝对是赢家。它是如何缩放的n
?(重复几次)
N <- 10 ^ (5:8) # 100x大于f0 t <- sapply(N, function(i) system.time(f4(i))[[3]]) plot(t ~ N, log="xy", type="b")
##警告在xy。Coords (x, y, xlabel, ylabel, log): 1 y值<= 0省略了##对数图
对不同的反应模式有什么解释吗?
经验教训:
*应用()
函数有助于避免显式预分配的需要,并使向量化的机会更加明显。这可能会带来较小的性能代价,但通常是值得的当数据太大而无法装入内存时,我们可以以块的形式遍历文件,或按字段或基因组位置将数据划分为子集。
迭代
open ()
,读取chunk(s),close ()
.yieldSize
参数Rsamtools: BamFile ()
GenomicFiles: reduceByYield ()
限制
利用特定于领域的格式
Rsamtools: ScanBamParam ()
Rsamtools: PileupParam ()
VariantAnnotation: ScanVcfParam ()
使用数据库
遍历文件:GenomicFiles: reduceByYield ()
suppressPackageStartupMessages({library(genome icfiles) library(genome icalignments) library(Rsamtools) library(TxDb.Hsapiens.UCSC.hg19.knownGene) library(rnaseqdata . hnrnc电脑.bam.chr14)}) yield <- #如何输入下一个数据块函数(X,…){readGAlignments(X)} map <- #对每个块函数(VALUE,…){olaps <- findOverlaps(VALUE, roi, type="within", ignore.strand=TRUE) count <- tabulate(subjectHits(olaps), subjectLength(olaps)) setNames(count, names(roi))} reduce <- ' + ' #如何合并映射块
改进:“yield factory”跟踪输入了多少记录
yieldFactory <- #返回一个带有局部状态函数的函数(){n_records <- 0L function(X,…){aln <- readGAlignments(X) n_records <<- n_records + length(aln) message(n_records) aln}}
感兴趣的区域,像bam文件中的染色体一样命名。
exByTx <- exonsBy(TxDb.Hsapiens.UCSC.hg19)。knownGene, tx)
迭代bam文件的函数。
count1 <- function(filename, roi) {message(filename) ##创建并打开BAM文件bf <- BamFile(filename, yieldSize=1000000) reduceByYield(bf, yieldFactory(), map, reduce, roi=roi)}
在行动
##实例bam文件库的路径(RNAseqData.HNRNPC.bam.chr14)chr14_BAMFILES count <- count1(bam[[1]], exByTx)
# # /用户/ ma38727 /图书馆/ R / 3.6 / Bioc / 3.10 / RNAseqData.HNRNPC.bam.chr14 / extdata / ERR127306_chr14.bam
## 800484
Hist (log10(count)) #掉0
类型 | 示例使用 | 的名字 | 包 |
---|---|---|---|
请全部 | 范围注释 | BedFile () |
rtracklayer |
.wig | 报道 | WigFile () ,BigWigFile () |
rtracklayer |
.gtf | 记录模型 | GTFFile () |
rtracklayer |
makeTxDbFromGFF () |
GenomicFeatures | ||
.2bit | 基因组序列 | TwoBitFile () |
rtracklayer |
.fastq | 阅读和品质 | FastqFile () |
ShortRead |
本演讲 | 对齐的读取 | BamFile () |
Rsamtools |
.tbx | 索引一样 | TabixFile () |
Rsamtools |
.vcf | 变量调用 | VcfFile () |
VariantAnnotation |
## rtracklayer menagerie suppressPackageStartupMessages(library(rtracklayer)) names(getClass("RTLFile")@subclasses)
## [1] "UCSCFile" "GFFFile" "BEDFile" ## [4] "WIGFile" "BigWigFile" "ChainFile" ## [7] "TwoBitFile" "FastaFile" "TabSeparatedFile" ## [10] "CompressedFile" "GFF1File" "GFF2File" "GFF2File" ## [13] "GFF3File" "BEDGraphFile" "BED15File" ## [16] "BEDPEFile" "NarrowPeakFile" "BroadPeakFile" ## [19] "BWFile" "2BitFile" " ggffile " ## [25] "BZ2File" "XZFile" ## [4] "TwoBitFile" "BigWigFile" "TabSeparatedFile" ## [10] "CompressedFile" "GFF1File" "GFF2File" ## [7] "TwoBitFile" "BigWigFile" "TabSeparatedFile" ## [10] "CompressedFile" "GFF1File" "GFF2File" ## [13] "GFF3File" "BEDGraphFile" "BED15File" ## [16] " beff3file " "BEDGraphFile" "BED15File"
笔记
open ()
,close ()
,进口()
/产量()
/读* ()
.bai
, bam索引);选择(“列”);限制(“行”)*文件列表()
类
reduceByYield ()
-遍历单个大文件bplapply ()
(BiocParallel) -在多个文件上并行执行独立操作GenomicFiles ()
reduceByRange ()
,reduceByFile ()
:将映射折叠为摘要表示VcfStack ()
VcfStack ?
需要注意的是
迭代/限制技术使内存需求处于控制之下,而并行计算将计算负载分布到各个节点。请记住,并行计算仍然受到每个节点上可用内存量的限制。
设置和拆除集群会产生开销,在分布式内存中进行计算时更是如此。对于小型计算,并行开销可能会超过性能没有提高的好处。
从并行执行中获益最多的作业是cpu密集型作业,它们操作的数据块适合内存。
BiocParallel为并行评估提供了一个标准化的接口,并支持主要的并行计算风格:单台计算机上的分叉和进程、AD hoc集群、批处理调度器和云计算。默认情况下,BiocParallel选择一个适合操作系统的并行后端,并且在Unix、Mac和Windows上都得到支持。
一般的想法:
bplapply ()
而不是拉普兰人()
论点BPPARAM
影响并行评估的发生方式
MulticoreParam ()
:单个(非windows)机器上的线程SnowParam ()
:同一或不同机器上的进程BatchJobsParam ()
:集群的资源调度程序这个小示例鼓励使用并行执行,并演示如何使用并行执行bplapply ()
可以顺便来一下吗拉普兰人
.
使用system.time ()
为了探索这需要多长时间来执行n
从1增加到10。使用相同的()
而且微基准测试比较备选方案f0 ()
而且f1 ()
为了正确性和性能。
有趣的
先睡1秒,然后再回来我
.
library(BiocParallel) fun <- function(i) {Sys.sleep(1) i} ## serial f0 <- function(n) lapply(seq_len(n), fun) ## parallel f1 <- function(n) bplapply(seq_len(n), fun)
BPREDO
BiocParallel“捕获并返回”错误以及成功的结果。本练习演示如何访问回溯()
以及如何使用“BPREDO”重新运行失败的任务。关于错误处理、日志记录和调试的详细信息见错误,日志和调试装饰图案。
param <- MulticoreParam(workers =3) # Windows: SnowParam(workers=3)
调用sqrt ()
函数跨越' X ';第二个元素是一个字符,会抛出和出错。
X <- list(1, "2", 3) res <- bplapply(X, sqrt, BPPARAM = param)
第一个错误:数学函数的非数字参数
它还可以捕获错误和部分计算结果
res <- bptry(bplapply(X, sqrt, BPPARAM=param)
## [[1]] ## [1] ## ## [[2]] ## ## traceback() available as 'attr(x, "traceback")' ## ## [[3]] ## [1] 1.732051
重复调用,重新运行失败的结果bplapply ()
这次的输入数据和部分结果是“BPREDO”。只有失败的值才会重新运行。
X.redo <- list(1,2,3) bplapply(X. redo)redo, sqrt, BPREDO = res)
([1]) # # # # # # # # [1] 1 [[2]] 1.414214 # # # # # # [1] [[3]] # # 1.732051 [1]
或者,切换到aSerialParam ()
并调试导致错误的特定元素。
> fun =函数(i){浏览器();sqrt(i)} > bplapply(X, fun, BPREDO=res, BPPARAM=SerialParam())恢复之前的计算…调用自:FUN(…)在# 1:浏览[1]>调试sqrt (i)浏览[2]>我[1]“2”浏览[2]>我= 2浏览[2]c [[1]] > [1] 1 [[2]] [1] 1.414214 1.732051 [[3]] [1]
BiocParallel使用futile.logger用于日志记录的包。该包有一个灵活的系统,用于过滤具有不同严重程度阈值的消息,如INFO、DEBUG、ERROR等(有关所有阈值的列表,请参阅?bpthreshold手册页)。BiocParallel捕获用无效语句写的消息。记录器格式以及写入stdout和stderr的消息。
这个函数执行一些参数检查,并具有DEBUG、WARN和info级别的日志消息。
FUN <- function(i) {flg .debug(paste0(" 'i'的值:",i)) if (!length(i)) {flog.debug。warn("'i'缺失")NA} else if (!我s(i, "numeric")) { flog.info("coercing to numeric") as.numeric(i) } else { i } }
在参数中打开日志记录,并将阈值设置为WARN。
bplapply(list(1, "2", integer()), FUN, BPPARAM = param)
将阈值降低到INFO和DEBUG(即使用bpthreshold < -
),以了解信息是如何按严重程度筛选的。
对于长时间运行的作业或未经测试的代码,设置时间限制是很有用的。的超时字段是允许每个工人完成任务的时间,以秒为单位。如果一项任务花费的时间超过超时worker返回一个错误。
超时可在参数构建时设置,
- SnowParam(timeout = 20
##类:SnowParam ## bpisup: FALSE;bpnworkers: 10;bptasks: 0;bpjobname: BPJOB ## bplog: FALSE;bpthreshold:信息;## bpRNGseed:;bptimeout: 20;bpprogressbar: FALSE ## bpexportglobals: TRUE ## bplogdir: NA ## bpresultdir: NA ## cluster type: SOCK
或者用setter:
Bptimeout (param) <- 2 param
##类:SnowParam ## bpisup: FALSE;bpnworkers: 10;bptasks: 0;bpjobname: BPJOB ## bplog: FALSE;bpthreshold:信息;## bpRNGseed:;bptimeout: 2;bpprogressbar: FALSE ## bpexportglobals: TRUE ## bplogdir: NA ## bpresultdir: NA ## cluster type: SOCK
使用此函数来探索一个数值向量的“X”值的不同_timeout_sbplapply ()
.' X '值小于超时当这些结束时成功返回阈值返回错误。
fun <- function(i) {Sys.sleep(i) i}
向员工分发文件:GenomicFiles: reduceByFile ()
前面的计数示例使用GenomicFiles: reduceByYield ()
它对单个文件进行操作,并实现yield, map, reduce范式。在这个练习中我们将使用GenomicFiles: reduceByFile ()
它使用bplaply ()
在底层并行操作多个文件。
的主要参数reduceByFile ()
是一组文件和一组范围。文件被发送到工作者,并根据范围提取数据子集。大部分工作都是在地图函数和一个可选的减少函数组合每个worker的输出。
suppressPackageStartupMessages({library(BiocParallel) library(genome icfiles) library(genome icalignments) library(Rsamtools)})
在Unix或Mac系统上配置MulticoreParam ()
有4个工人。打开日志记录并将超时设置为60秒。
param <- MulticoreParam(4, log = TRUE, timeout = 60)
在Windows上执行同样的操作SnowParam ()
:
- SnowParam(4, log = TRUE, timeout = 60)
指向bam文件的集合。
库(RNAseqData.HNRNPC.bam.chr14) fls <- BamFileList(fls)
定义范围(感兴趣的区域)可以限制工作线程上的数据量,并控制内存需求。
ranges <- GRanges("chr14", IRanges(c(28477797, 29527797, 32448354), c(29477797, 30527797, 33448354)))
的地图函数读取记录并计数重叠。readGAlignments ()
类中定义的范围的任何部分重叠的bam记录scanBamParam(例如,它们可以重叠开始或结束)。一旦我们拿到了记录R,我们只想计算那些“在”范围内的。
MAP <- function(range, file,…){library(genome icalignments) ## readGAlignments(), ScanBamParam() param= ScanBamParam(which=range) ## restriction gal = readGAlignments(file, param=param) ## overlaps olaps <- findOverlaps(gal, range, type="within",忽略.strand=TRUE) tabulate(subjectHits(olaps), subjectLength(olaps))}
数……
cts <- reduceByFile(ranges, fls, MAP, BPPARAM = param)
## ############### LOG OUTPUT ############### ## Task: 1 ## Node: 1 ##时间戳:2019-07-28 13:49:01 ## Success: TRUE ####任务持续时间:## user system elapsed ## 0.426 0.144 0.573 ####内存已使用:##已使用(Mb) gc trigger (Mb) limit (Mb) max used (Mb) ## Ncells 8253002 440.8 13065045 697.8 NA 13065045 697.8 ## Vcells 17094705 130.5 53213824 406.0 32768 202994621 1548.8 ####日志消息:## INFO [2019-07-28 13:49:00] loading futile。日志包## ## stderr和stdout:
## ############### LOG OUTPUT ############### ## Task: 2 ## Node: 2 ##时间戳:2019-07-28 13:49:02 ## Success: TRUE ####任务持续时间:## user system elapsed ## 0.426 0.147 0.578 ####内存使用:##使用(Mb) gc trigger (Mb) limit (Mb) max used (Mb) ## Ncells 8254739 440.9 13065045 697.8 NA 13065045 697.8 ## Vcells 17098950 130.5 53213824 406.0 32768 202994621 1548.8 ####日志消息:## INFO [2019-07-28 13:49:00] loading futile. .日志包## ## stderr和stdout:
## ############### LOG OUTPUT ############### ## Task: 3 ## Node: 3 ##时间戳:2019-07-28 13:49:02 ## Success: TRUE ####任务持续时间:## user system elapsed ## 0.423 0.144 0.572 ####内存使用:##使用(Mb) gc trigger (Mb) limit (Mb) max used (Mb) ## Ncells 8254772 440.9 13065045 697.8 NA 13065045 697.8 ## Vcells 17099033 130.5 53213824 406.0 32768 202994621 1548.8 ####日志消息:## INFO [2019-07-28 13:49:00] loading futile. # Ncells 8254772 440.9 13065045 697.8 NA 13065045 697.8 ## Vcells 17099033 130.5 53213824 406.0 32768 202994621 1548.8 ####日志包## ## stderr和stdout:
## ############### LOG OUTPUT ############### ## Task: 4 ## Node: 4 ##时间戳:2019-07-28 13:49:02 ## Success: TRUE ####任务持续时间:## user system elapsed ## 0.426 0.145 0.574 ####内存已使用:##已使用(Mb) gc trigger (Mb) limit (Mb) max used (Mb) ## Ncells 8254805 440.9 13065045 697.8 NA 13065045 697.8 ## Vcells 17099114 130.5 53213824 406.0 32768 202994621 1548.8 ####日志消息:## INFO [2019-07-28 13:49:00] loading futile。日志包## ## stderr和stdout:
结果是一个与文件数量相同长度的列表。
长度(cts)
## [1]
每个列表元素都是范围数量的长度。
长度(cts)
## err127306 err127307 err127308 err127309 err127302 err127303 err127304 ## 3 3 3 3 3 ## err127305 ## 3
提取每个范围的计数表[[
;使用simplifyToArray ()
得到一个计数矩阵
cts ([1])
([1]) # # # # 429 # # # #[1][[2]] 22 # # # # # #[1][[3]] # # 1338年[1]
simplify2array (cts)
## err127306 err127307 err127308 err127309 err127302 err127302 err127303 err127304 ## [1,] 429 477 508 452 516 721 713 ## [2,] 22 18 37 24 14 26 30 ## [3,] 1338 1938 1618 1683 1988 2526 2372 ## err127305 ## [1,] 544 ## [2,] 20 ## [3,] 1785
劳伦斯,M,摩根,M. 2014。可扩展基因组与R和Bioconductor。统计科学2014,Vol. 29, No. 2, 214-226。http://arxiv.org/abs/1409.2864v1
BiocParallel://www.andersvercelli.com/packages/release/bioc/html/BiocParallel.html
GenomicFiles://www.andersvercelli.com/packages/release/bioc/html/GenomicFiles.html