Optimization R优化:在这种情况下如何避免for循环?

Optimization R优化:在这种情况下如何避免for循环?,optimization,r,intersection,bioinformatics,Optimization,R,Intersection,Bioinformatics,我试图在R中做一个简单的基因组轨迹交叉,遇到了主要的性能问题,可能与我使用for循环有关 在这种情况下,我以100bp的间隔预先定义了窗口,并试图计算mylist中的注释覆盖了每个窗口的多少。从图形上看,它看起来像这样: 0 100 200 300 400 500 600 windows: |-----|-----|-----|-----|-----|-----| mylist: |-| |-----------| 所以我写了一

我试图在R中做一个简单的基因组轨迹交叉,遇到了主要的性能问题,可能与我使用for循环有关

在这种情况下,我以100bp的间隔预先定义了窗口,并试图计算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
中指定上限,但如果您事先不知道上限,则不难再次获得上限。

正确的做法是使用bioconductor
IRanges
包,该包使用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