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

时间:2010-03-25 17:23:18

标签: optimization r intersection bioinformatics

我正在尝试在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

当然,这用于比我在此提供的示例大得多的数据集。通过一些分析,我可以看到瓶颈在for循环中,但是我使用* apply函数对其进行矢量化的笨拙尝试导致代码运行速度更慢一些数量级。

我想我可以在C中写一些东西,但如果可能的话,我想避免这样做。任何人都可以提出另一种方法来加速这种计算吗?

5 个答案:

答案 0 :(得分:6)

“正确”的做法是使用bioconductor IRanges包,它使用IntervalTree数据结构来表示这些范围。

将两个对象都放在自己的IRanges对象中,然后使用findOverlaps函数获胜。

在此处获取:

http://www.bioconductor.org/packages/release/bioc/html/IRanges.html

通过by,包的内部用C语言编写,所以它超级快。

修改

第二个想法,它并不像我建议的那样(只有一个班轮),但如果你在基因组间隔(或其他类型)工作,你肯定应该开始使用这个库......你可能需要做一些固定操作和事情。抱歉,没有时间提供确切的答案。

我只是认为将这个库指向你是很重要的。

答案 1 :(得分:4)

所以我不完全确定为什么第三和第四个窗口不是100和20,因为这对我来说更有意义。以下是该行为的一个内容:

Reduce('+', lapply(mylist, function(x) hist(x[1]:x[2], breaks = (0:6) * 100, plot = F)$counts)) 

请注意,您需要在breaks中指定上限,但如果您事先不知道它,则应该很难再获取它。

答案 2 :(得分:4)

答案 3 :(得分:1)

我想我让它变得更加复杂...... 在这么小的数据集中,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)

修改

消除eval

的修改
g <- llply(mylist, function(x) {
ldply((floor(x[1]/100)+1) : (floor(x[2]/100)+1), function(z){
        t(matrix(c(z,min(z*100, x[2]) - max((z-1)*100, x[1]) + 1),nrow=2))
    })          
})

for(i in 1:length(g)){
    windows[unlist(g[[i]][1])] <- unlist(g[[i]][2])
}

答案 4 :(得分:0)

我没有一个好主意,但你可以摆脱内循环,并加快一些事情。请注意,如果窗口完全处于mylist间隔,那么您只需将100添加到相应的windows元素。因此,只有st - 和sp个窗口需要特殊处理。

  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
    }       
  }

我生成了一个更大的列表:

  cuts <- sort(sample(1:10000, 70))  # random interval endpoints
  mylist <- split(cuts, gl(35,2))

并且对于该版本的1000次重复获得1.08秒,而对于原始的1000次重复获得1.72秒。对于真实数据,加速将取决于mylist中的间隔是否远远超过100。

顺便说一下,可以将内部循环重写为单独的函数,然后lapply将其重写为mylist,但这并不能使其更快地运行。