作者:Valerie Obenchain (vobencha@fredhutch.org)、马伟民(mtmorgan@fredhutch.org)
时间:2015年7月22日
本节的目标是强调如何编写正确、健壮和高效的R代码。
相同的()
,all.equal ()
)NA
值,…system.time ()
或者是微基准测试包中。Rprof ()
函数或包,例如lineprof或aprof向量化——操作向量,而不是显式循环
x <- 1:10 log(x) ## NOT for (i in seq_along) x[i] <- log(x[i])
## [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
[6] 0.610156070 0.326579479 0.319540555 0.182668253 0.078093233 ## [6] 0.056413616 0.021594612 0.017351821 0.006025564 0.004570092
为
循环lm.fit ()
而不是重复拟合相同的设计矩阵。汇总()
,rowSums ()
和朋友,%, %
,……这是一个明显低效的函数:
f0 < -函数(n = 2) {# # stopifnot (is.integer (n) & &(长度(n) = = 1) & & # # ! is.na (n) & & (n > 0))结果< -数字()(我在seq_len (n))结果[[我]]< - *日志(i)结果}
使用system.time ()
研究这个算法是如何扩展的n
,重点关注经过的时间。
system.time (f0 (10000))
##用户系统运行## 0.150
n < - 1000 * seq(2) 1, 20日t < -酸式焦磷酸钠(n,函数(i) system.time (f0(我)[[3]])情节(t ~ n、类型=“b”)
记住当前的“正确”值和一个大概的时间
N <- 10000系统。时间(期望<- f0(n))
##用户系统运行## 0.141 0.000 0.142
(预计)
## [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[[i]] <- log(i) a * result}相同(期望,F1 (n))
##[1]真
库(微基准)微基准(f0(n), f1(n), times=5)
##单位:毫秒## expr min lq mean median uq max 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
采用“预分配和填充”策略
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)
##单位:毫秒## expr min lq mean median uq max neval ## f0(n) 102.20437 162.75624 152.84236 163.04595 167.98829 168.21692 5 ## f2(n) 11.05322 11.35959 13.71776 13.29358 15.93749 16.94491 5
使用一个*应用()
函数,以避免必须显式预分配,并使向量化的机会更加明显。
F3 <-函数(n, a=2) a * sapply(seq_len(n), log)相同(预期,F3 (n))
##[1]真
微基准测试(f0(n), f2(n), f3(n), times=10)
##单位:毫秒## expr min lq mean median uq max ## 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 <-函数(n, a=2) a * log(seq_len(n))相同(预期,F4 (n))
##[1]真
微基准(f0(n), f3(n), f4(n), times=10)
##单位:微秒## expr min lq mean median uq max ## 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]]) plot(t ~ N, log="xy", type="b")
对不同的反应模式有什么解释吗?
经验教训:
*应用()
函数有助于避免显式预分配的需要,并使向量化的机会更加明显。这可能会带来很小的性能成本,但通常是值得的当数据太大而无法装入内存时,我们可以以块的形式遍历文件,或者按字段或基因组位置对数据进行子集。
迭代-块-open ()
,读取数据块,close ()
.——例如,yieldSize
参数Rsamtools: BamFile ()
-框架:GenomicFiles: reduceByYield ()
限制-限制感兴趣的列和/或行-利用领域特定的格式- BAM文件和Rsamtools: ScanBamParam ()
- BAM文件和Rsamtools: PileupParam ()
—VCF文件和VariantAnnotation: ScanVcfParam ()
-使用数据库
遍历文件:' GenomicFiles::reduceByYield()
suppressPackageStartupMessages({library(GenomicFiles) library(GenomicAlignments) library(Rsamtools) library(txdb . hsapiens . ucc .hg19. knowngene)}) yield <- #如何输入下一个数据块函数(X,…){readGAlignments(X)} map <- #如何对每个块函数(VALUE,…), roi) {olaps <- findOverlaps(VALUE, roi, type="within", ignore.strand=TRUE) count <- tabulate(subjectHits(olaps), subjectLength(olaps)) setNames(count, names(roi))} reduce <- ' + ' #如何组合映射块
改进:“产量工厂”跟踪输入了多少记录
yieldFactory <- #返回一个具有本地状态的函数function() {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. exByTx <- exonsBy)。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/data/vobencha/LargeData/srarchive/SRR1039508_sorted. "count <- count1(bam, exByTx)
类型 | 示例使用 | 的名字 | 包 |
---|---|---|---|
请全部 | 范围注释 | 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”##[7]“CompressedFile”“GFF1File”“GFF2File”“GFF2File”##[13]“GFF3File”“BEDGraphFile”“BED15File”##[16]“BWFile”“2BitFile”“GTFFile”##[19]“GVFFile”“GZFile”“BGZFile”“XZFile”##[7]“BZ2File”“XZFile”
笔记
open ()
,close ()
,进口()
/产量()
/读* ()
.bai
, bam索引);选择(“列”);限制(“行”)*文件列表()
类
reduceByYield ()
-遍历单个大文件bplapply ()
(BiocParallel) -并行地对多个文件执行独立的操作GenomicFiles ()
reduceByRange ()
,reduceByFile ()
:将映射折叠为摘要表示有几个警告-
迭代/限制技术使内存需求处于控制之下,而并行求值则将计算负载分布到各个节点。请记住,并行计算仍然受到每个节点上可用内存量的限制。
设置和拆除集群有开销,在分布式内存中计算时更是如此。对于小型计算,并行开销可能超过收益,而性能没有提高。
从并行执行中受益最多的作业是cpu密集型作业,操作的数据块适合内存。
BiocParallel为并行计算提供标准化接口,并支持主要的并行计算风格:单台计算机上的分支和进程、AD hoc集群、批处理调度器和云计算。默认情况下,BiocParallel选择适合操作系统的并行后端,支持Unix, Mac和Windows。
一般的想法:
bplapply ()
而不是拉普兰人()
论点BPPARAM
影响并行计算的发生方式
MulticoreParam ()
:单个(非windows)机器上的线程SnowParam ()
:同一或不同机器上的进程BatchJobsParam ()
:集群的资源调度程序DoparParam ()
:并行执行foreach ()
这个小示例激发了并行执行的使用,并演示了如何使用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 '。错误处理、日志记录和调试的详细信息见错误,日志和调试装饰图案。
multicorepam (workers = 3
调用sqrt ()
函数跨越' X ';第二个元素是一个字符,将抛出和错误。
X <- list(1, "2", 3) res <- bplapply(X, sqrt, BPPARAM = param) res
与错误消息一起,回溯信息存储在返回列表元素中,可以使用attr ()
:
attr (res[[2]],“回溯”)
通过重复调用来重新运行失败的结果bplapply ()
这一次用修正的输入数据和部分结果作为“BPREDO”。
X.redo <- list(1,2,3) bplapply(X。redo, sqrt, BPREDO = res)
BiocParallel使用futile.logger用于日志记录的包。该包有一个灵活的系统,用于过滤不同严重阈值的消息,如INFO、DEBUG、ERROR等(有关所有阈值的列表,请参阅?bpthreshold手册页)。BiocParallel捕获在futile中写入的消息。记录器格式以及写入stdout和stderr的消息。
这个函数做一些参数检查,并有DEBUG, WARN和info级别的日志消息。
FUN <- function(i) {flag .debug(paste0(" 'i'的值:",i)) if (!length(i)) {flog.debug(' ', i'))警告("'i' is missing") NA} else if (!我s(i, "numeric")) { flog.info("coercing to numeric") as.numeric(i) } else { i } }
在参数中打开日志记录,并将阈值设置为WARN。
- SnowParam(3, log = TRUE, threshold = "WARN") bplapply(list(1, "2", integer()), FUN, BPPARAM = param)
将阈值降低到INFO和DEBUG(即使用bpthreshold < -
),以了解如何按严重程度筛选讯息。
对于长时间运行的作业或未经测试的代码,设置时间限制可能会很有用。的超时字段是允许每个工人完成一项任务的时间,单位为秒。如果一项任务花费的时间超过超时worker返回一个错误。
超时可在参数构造时设置,
- SnowParam(timeout = 20
## bpjobname:BPJOB;bpworkers: 2;bptasks: 0;bptimeout: 20;bpRNGseed:;bpisup:FALSE ## bplog:FALSE;bpthreshold:信息;bplogdir:NA ## bpstopOnError:FALSE;bpprogressbar:FALSE ## bpresultdir:NA ##集群类型:SOCK
或者使用setter:
Bptimeout (param) <- 2个参数
## bpjobname:BPJOB;bpworkers: 2;bptasks: 0;bptimeout: 2;bpRNGseed:;bpisup:FALSE ## bplog:FALSE;bpthreshold:信息;bplogdir:NA ## bpstopOnError:FALSE;bpprogressbar:FALSE ## bpresultdir:NA ##集群类型: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(genomic icfiles) library(genomic icalignments) library(Rsamtools)})
在Unix或Mac上配置aMulticoreParam ()
有4名工人。打开日志记录并设置60秒的超时。
- multicorepam (4, log = TRUE, timeout = 60)
在Windows上执行相同的操作SnowParam ()
:
- SnowParam(4, log = TRUE, timeout = 60)
指向bam文件的集合。
fls <- dir("/home/ubuntu/data/vobencha/LargeData/copynumber", "。bam$", full=TRUE) names(fls) <- basename(fls) bfl <- BamFileList(fls)
定义范围(感兴趣的区域)可以限制工作线程上的数据量,并控制内存需求。我们将在6号染色体上的主要组织相容性复合体位点上使用一组范围。
range <- GRanges("chr6", IRanges(c(28477797, 29527797, 32448354), c(29477797, 30527797, 33448354)))))
的地图函数读取记录并计数重叠。readGAlignments ()
类中定义的范围的任何部分重叠的bam记录scanBamParam(也就是说,它们可能在开头或结尾重叠)。一旦我们拿到记录R,我们只想计算“在”范围内的那些。
MAP <- function(range, file,…){library(GenomicAlignments) ## readGAlignments(), ScanBamParam() param= ScanBamParam(which=range) ##限制gal = readGAlignments(file, param=param) ##日志消息flag .info(paste0("file: ", basename(file)))) flag .debug(paste0("records: ", length(gal))) ##重叠olaps <- findOverlaps(gal, range, type="within", ignore.strand=TRUE) tabulate(subjectHits(olaps), subjectLength(olaps))}
数……
cts <- reduceByFile(ranges, fls, MAP, BPPARAM = param)
结果是一个与文件数相同长度的列表。
长度(cts)
每个列表元素都是范围数的长度。
elementLengths (cts)
每个范围的计数表用'[['提取:
cts ([1])
劳伦斯,M.和摩根,M. 2014。可扩展基因组与R和Bioconductor。统计科学,2014年第29卷第2期,214-226。http://arxiv.org/abs/1409.2864v1
BiocParallel://www.anjoumacpherson.com/packages/release/bioc/html/BiocParallel.html
GenomicFiles://www.anjoumacpherson.com/packages/release/bioc/html/GenomicFiles.html