Optimization R优化:在这种情况下如何避免for循环?
我试图在R中做一个简单的基因组轨迹交叉,遇到了主要的性能问题,可能与我使用for循环有关 在这种情况下,我以100bp的间隔预先定义了窗口,并试图计算mylist中的注释覆盖了每个窗口的多少。从图形上看,它看起来像这样:Optimization R优化:在这种情况下如何避免for循环?,optimization,r,intersection,bioinformatics,Optimization,R,Intersection,Bioinformatics,我试图在R中做一个简单的基因组轨迹交叉,遇到了主要的性能问题,可能与我使用for循环有关 在这种情况下,我以100bp的间隔预先定义了窗口,并试图计算mylist中的注释覆盖了每个窗口的多少。从图形上看,它看起来像这样: 0 100 200 300 400 500 600 windows: |-----|-----|-----|-----|-----|-----| mylist: |-| |-----------| 所以我写了一
0 100 200 300 400 500 600
windows: |-----|-----|-----|-----|-----|-----|
mylist: |-| |-----------|
所以我写了一些代码来实现这一点,但速度相当慢,已经成为我代码中的瓶颈:
##window for each 100-bp segment
windows <- numeric(6)
##second track
mylist = vector("list")
mylist[[1]] = c(1,20)
mylist[[2]] = c(120,320)
##do the intersection
for(i in 1:length(mylist)){
st <- floor(mylist[[i]][1]/100)+1
sp <- floor(mylist[[i]][2]/100)+1
for(j in st:sp){
b <- max((j-1)*100, mylist[[i]][1])
e <- min(j*100, mylist[[i]][2])
windows[j] <- windows[j] + e - b + 1
}
}
print(windows)
[1] 20 81 101 21 0 0
每个100 bp段的窗口
windows我想我把它弄得更复杂了。。。 System.time在如此小的数据集中无法帮助我进行性能评估
windows <- numeric(6)
mylist = vector("list")
mylist[[1]] = c(1,20)
mylist[[2]] = c(120,320)
library(plyr)
l_ply(mylist, function(x) {
sapply((floor(x[1]/100)+1) : (floor(x[2]/100)+1), function(z){
eval.parent(parse(text=paste("windows[",z,"] <- ",
min(z*100, x[2]) - max((z-1)*100, x[1]) + 1,sep="")),sys.nframe())
})
})
print(windows)
我没有什么好主意,但是你可以去掉内部循环,把事情加快一点。请注意,如果一个窗口在mylist间隔内完全落下,则只需将100添加到相应的
windows
元素中即可。因此,只有st
-th和sp
-th窗口需要特殊处理
windows <- numeric(100)
for(i in 1:length(mylist)){
win <- mylist[[i]] # for cleaner code
st <- floor(win[1]/100)+1
sp <- floor(win[2]/100)+1
# start and stop are within the same window
if (sp == st){
windows[st] <- windows[st] + (win[2]%%100) - (win[1]%%100) +1
}
# start and stop are in separate windows - take care of edges
if (sp > st){
windows[st] <- windows[st] + 100 - (win[1]%%100) + 1
windows[sp] <- windows[sp] + (win[2]%%100)
}
# windows completely inside win
if (sp > st+1){
windows[(st+1):(sp-1)] <- windows[(st+1):(sp-1)] + 100
}
}
windows所以我不能完全确定为什么第三个和第四个窗口不是100和20,因为这对我来说更有意义。对于这种行为,这里有一个简单的解释:
Reduce('+', lapply(mylist, function(x) hist(x[1]:x[2], breaks = (0:6) * 100, plot = F)$counts))
请注意,您需要在breaks
中指定上限,但如果您事先不知道上限,则不难再次获得上限。正确的做法是使用bioconductorIRanges
包,该包使用IntervalTree数据结构来表示这些范围
将两个对象都放在各自的IRanges
对象中,然后使用findOverlaps
功能获胜
在这里获取:
顺便说一句,包的内部是用C编写的,所以速度非常快
编辑
再想一想,这并不像我建议的那样是一个扣篮(一行),但如果你在基因组间隔(或其他类型)工作,你肯定应该开始使用这个库。。。您可能需要执行一些设置操作和其他操作。抱歉,我没有时间提供确切的答案
我只是觉得向您指出这个库很重要。好吧,所以我在这方面浪费了太多时间,但仍然只有3倍的加速。谁能打败这个
守则:
my <- do.call(rbind,mylist)
myFloor <- floor(my/100)
myRem <- my%%100
#Add intervals, over counting interval endpoints
counts <- table(do.call(c,apply(myFloor,1,function(r) r[1]:r[2])))
windows[as.numeric(names(counts))+1] <- counts*101
#subtract off lower and upper endpoints
lowerUncovered <- tapply(myRem[,1],myFloor[,1],sum)
windows[as.numeric(names(lowerUncovered))+1] <- windows[as.numeric(names(lowerUncovered))+1] - lowerUncovered
upperUncovered <- tapply(myRem[,2],myFloor[,2],function(x) 100*length(x) - sum(x))
windows[as.numeric(names(upperUncovered))+1] <- windows[as.numeric(names(upperUncovered))+1] - upperUncovered
非常令人沮丧的是,系统时间基本上为0,但观测到的时间太长了。我打赌如果你真的去C,你会得到50-100倍的加速。这似乎是一个问题,可以通过Bioconductor中的IRanges
包解决。这可能是一个很好的起点。hrmm-感谢指针-似乎很有希望。我使用了比示例稍大的数据进行了尝试,这比原始的要长约10倍:(@Aniko.显然eval
对性能有很大的影响。你能想出另一种方法来访问windows
变量吗?可以使用“[没有eval
的修改花费的时间更长。@Aniko我放弃:)顺便说一句,谢谢你的语法。我以前没有见过它,现在,我无法在帮助中找到它。它必须是Reduce
,而不是do.call
,但是这种方法非常慢(虽然很优雅)。感谢Reduce上的技巧!我尝试过,它看起来并不那么慢:>system.time(replicate)(Reduce(+),lappy(mylist,函数(x)hist(x[1]:x[2],中断=剪切,绘图=F)$counts)),1000)利用我们的系统需要0.03 0.00 0.03这是一个非常优雅的功能,但速度要慢得多。在一组10个窗口和6个缺口上,重复1000次,这需要6.58秒,而原始功能需要0.18秒。我正在查看整个基因组,并将考虑数百万条注释,所以这并不重要“t scale.BTW-关于第三个和第四个窗口,你是对的-我已经修复了一个小错误。为什么Reduce
比do好。调用?据我所知Reduce
执行类似于的操作(元素中的I)结果谢谢-在这和andrewj上面的建议之间,我怀疑IRanges可能是一条出路。这将需要对我的代码进行一些广泛的重写,所以我现在就开始,并将很快报告。是的,使用IRanges使这个项目更容易编码,速度也更快。谢谢你的提示。
Reduce('+', lapply(mylist, function(x) hist(x[1]:x[2], breaks = (0:6) * 100, plot = F)$counts))
my <- do.call(rbind,mylist)
myFloor <- floor(my/100)
myRem <- my%%100
#Add intervals, over counting interval endpoints
counts <- table(do.call(c,apply(myFloor,1,function(r) r[1]:r[2])))
windows[as.numeric(names(counts))+1] <- counts*101
#subtract off lower and upper endpoints
lowerUncovered <- tapply(myRem[,1],myFloor[,1],sum)
windows[as.numeric(names(lowerUncovered))+1] <- windows[as.numeric(names(lowerUncovered))+1] - lowerUncovered
upperUncovered <- tapply(myRem[,2],myFloor[,2],function(x) 100*length(x) - sum(x))
windows[as.numeric(names(upperUncovered))+1] <- windows[as.numeric(names(upperUncovered))+1] - upperUncovered
mylist = vector("list")
for(i in 1:20000){
d <- round(runif(1,,500))
mylist[[i]] <- c(d,d+round(runif(1,,700)))
}
windows <- numeric(200)
new_code <-function(){
my <- do.call(rbind,mylist)
myFloor <- floor(my/100)
myRem <- my%%100
counts <- table(do.call(c,apply(myFloor,1,function(r) r[1]:r[2])))
windows[as.numeric(names(counts))+1] <- counts*101
lowerUncovered <- tapply(myRem[,1],myFloor[,1],sum)
windows[as.numeric(names(lowerUncovered))+1] <- windows[as.numeric(names(lowerUncovered))+1] - lowerUncovered
upperUncovered <- tapply(myRem[,2],myFloor[,2],function(x) 100*length(x) - sum(x))
windows[as.numeric(names(upperUncovered))+1] <- windows[as.numeric(names(upperUncovered))+1] - upperUncovered
#print(windows)
}
#old code
old_code <- function(){
for(i in 1:length(mylist)){
st <- floor(mylist[[i]][1]/100)+1
sp <- floor(mylist[[i]][2]/100)+1
for(j in st:sp){
b <- max((j-1)*100, mylist[[i]][1])
e <- min(j*100, mylist[[i]][2])
windows[j] <- windows[j] + e - b + 1
}
}
#print(windows)
}
system.time(old_code())
system.time(new_code())
> system.time(old_code())
user system elapsed
2.403 0.021 2.183
> system.time(new_code())
user system elapsed
0.739 0.033 0.588