我收集了一组不同长度的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)这在小数据集上很好,但我的实际数据更像是500万读取,长度从大约20万到1不等(较长的读取更少),我对10万的N50感兴趣,然后就会变得非常慢。
这个例子更接近现实。在我的台式机上需要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)因此,我感兴趣的任何想法,提示,或诀窍,以显着优化这一点。看来这是可能的,但我没有主意了。
发布于 2017-09-19 14:42:34
随着n随i的减少,您应该使用二进制搜索算法。
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))发布于 2017-09-19 07:57:41
由于您的数据是按DNA/read长度排序的,所以您可以避免测试每一行。相反,您可以在每次迭代中迭代和测试有限数量的行(合理间距)(例如使用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行。通过重新定义范围,得到最接近的区域并“专注于”。当您不能增加粒度时停止。
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.https://stackoverflow.com/questions/46292438
复制相似问题