优化R中向量的每个累积子集的计算

时间:2017-09-19 05:25:31

标签: r loops optimization vectorization

我有一系列不同长度的DNA测序读数,从最长到最短排序。我想知道我可以在一个集合中包含的最大数量的读取,使得该集合的N50高于某个阈值t

对于任何给定的读取集,数据总量只是读取长度的累积总和。 N50被定义为读取的长度,使得一半数据包含在至少那么长的读取中。

下面我有一个解决方案,但是对于非常大的读取集来说它很慢。我尝试对它进行矢量化,但速度较慢(可能是因为我的阈值通常相对较大,因此我的解决方案会在相当早的时候停止计算)。

这是一个有效的例子:

df = data.frame(l = 100:1) # read lengths
df$cs = cumsum(df$l) # getting the cumulative sum is easy and quick

t = 95 # let's imagine that this is my threshold N50

for(i in 1:nrow(df)){
    N50 = df$l[min(which(df$cs>df$cs[i]/2))]
    if(N50 < t){ break }
}

# the loop will have gone one too far, so I subtract one
number.of.reads = as.integer(i-1)

这在小数据集上运行良好,但我的实际数据更像是5米读取,长度从~200,000到1不等(读取时间更长),我对N50为100,000感兴趣,然后它变得漂亮慢。

这个例子更贴近现实。我的桌面需要大约15秒。

l = ceiling(runif(100000, min = 0, max = 19999))
l = sort(l, decreasing = T)

df = data.frame(l = l)
df$cs = cumsum(df$l)

t = 18000

for(i in 1:nrow(df)){
    n = df$l[min(which(df$cs>df$cs[i]/2))]
    if(n < t){ break }
}

result = as.integer(i-1)

所以,我对任何想法,提示或技巧感兴趣,以显着优化这一点。看起来这应该是可能的,但我没有想法。

2 个答案:

答案 0 :(得分:0)

由于您的数据按DNA /读取长度排序,因此您可以避免测试每一行。相反,您可以在每次迭代时迭代并测试有限数量的行(合理间隔)(例如,使用while()),因此逐渐接近您的解决方案。这应该会使事情变得更快。只需确保一旦接近解决方案,就停止迭代。

这是您的解决方案

set.seed(111)
l = ceiling(runif(100000, min = 0, max = 19999))
l = sort(l, decreasing = T)

df = data.frame(l = l)
df$cs = cumsum(df$l)

t = 18000

for(i in 1:nrow(df)){
  n = df$l[min(which(df$cs>df$cs[i]/2))]
  if(n < t){ break }
}

result = as.integer(i-1)
result 
# 21216, in ~29 seconds

不要测试每一行,而是设置范围

i1 <- 1
i2 <- nrow(df)
i.range <- as.integer(seq(i1, i2, length.out = 10))

现在,只测试这10行。得到最近的一个,并且&#34;专注于&#34;通过重新定义范围。当你无法增加粒度时停止。

while(sum(duplicated(i.range))==0){
  for(i in 1:length(i.range)){
    N50 = df$l[min(which(df$cs>df$cs[i.range[i]]/2))]
    if(N50 < t){ break }
  }

  #update i1 and i2
  i1 <- i.range[(i-1)]
  i2 <- i.range[i]
  i.range <- as.integer(seq(i1, i2, length.out = 10))

}

i.range <- seq(i1, i2, by=1)
for(i in i.range){
  N50 = df$l[min(which(df$cs>df$cs[i]/2))]
  if(N50 < t){ break }
}
result <- as.integer(i-1)
result 
#21216, in ~ 0.06 seconds

Same result in a fraction of the time.

答案 1 :(得分:0)

n随着i而减少,您应该使用binary search algorithm

binSearch <- function(min, max) {
  print(mid <- floor(mean(c(min, max))))
  if (mid == min) {
    if (df$l[min(which(df$cs>df$cs[min]/2))] < t) {
      return(min - 1)
    } else {
      return(max - 1)
    }
  }

  n = df$l[min(which(df$cs>df$cs[mid]/2))]
  if (n >= t) {
    return(binSearch(mid, max))
  } else {
    return(binSearch(min, mid))
  }
}

然后,只需致电

binSearch(1, nrow(df))