我有一系列不同长度的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)
所以,我对任何想法,提示或技巧感兴趣,以显着优化这一点。看起来这应该是可能的,但我没有想法。
答案 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))