我在R中建立了一些函数,根据自定义的光谱相似性函数和化合物保留指数(即洗脱时间)的匹配,将化学质谱(一个具有整数质量和强度的两列矩阵)与这类光谱的库相匹配(见这里的例子,http://webbook.nist.gov/cgi/cbook.cgi?ID=C630035&Mask=200)。为此,必须将每个记录的列表元素"RI“与库中的列表元素”RI“进行比较,当绝对偏差小于给定的公差时,它应该在我的记录中添加最佳的光谱库匹配。下面是我为此编写的一些代码,但问题是,就我的目的而言,它太慢了(我通常有大约1000个样本谱和20万个库谱)。我尝试过并行化,但这似乎也没有多大帮助。对于如何使下面的代码更高效,例如使用更多的矢量化,或者使用内联C代码,或者其他一些R技巧,有什么想法吗?我知道这方面的一般建议,但不太明白如何在这种情况下轻松地实现它(不幸的是,我还不太精通C).有什么想法或建议吗?哦,是的,在使用sfLapply时,我如何添加进度条呢?也许首先将我的光谱放在一个大的(稀疏的,因为有很多零)矩阵中,以避免谱相似性函数中的merge步骤,或者使用附加的准则,例如只考虑在查询谱中的最大/最强烈的峰与库谱的质量相同时(或者包含在库谱中的五个最大峰中)?无论如何,任何关于如何加快这一任务的想法将是非常感谢的!
编辑:我仍然有一个剩余的查询,就是如何避免在函数addbestlibmatches1中复制完整的示例记录recs,而是只更改与库匹配的记录?此外,传递有保留索引匹配的库记录的选择可能并不有效(在函数addbestlibmatch中)。我有什么办法避免这件事吗?
# EXAMPLE DATA
rec1=list(RI=1100,spectrum=as.matrix(cbind(mz=c(57,43,71,41,85,56,55,70,42,84,98,99,39,69,58,113,156),int=c(999,684,396,281,249,173,122,107,94,73,51,48,47,47,37,33,32))))
randrec=function() list(RI=runif(1,1000,1200),spectrum=as.matrix(cbind(mz=seq(30,600,1),int=round(runif(600-30+1,0,999)))))
# spectral library
libsize=2000 # my real lib has 200 000 recs
lib=lapply(1:libsize,function(i) randrec())
lib=append(list(rec1),lib)
# sample spectra
ssize=100 # I usually have around 1000
s=lapply(1:ssize,function(i) randrec())
s=append(s,list(rec1)) # we add the first library record as the last sample spectrum, so this should match
# SPECTRAL SIMILARITY FUNCTION
SpecSim=function (ms1,ms2,log=F) {
alignment = merge(ms1,ms2,by=1,all=T)
alignment[is.na(alignment)]=0
if (nrow(alignment)!=0) {
alignment[,2]=100*alignment[,2]/max(alignment[,2]) # normalize base peak intensities to 100
alignment[,3]=100*alignment[,3]/max(alignment[,3])
if (log==T) {alignment[,2]=log2(alignment[,2]+1);alignment[,3]=log2(alignment[,3]+1)} # work on log2 intensity scale if requested
u = alignment[,2]; v = alignment[,3]
similarity_score = as.vector((u %*% v) / (sqrt(sum(u^2)) * sqrt(sum(v^2))))
similarity_score[is.na(similarity_score)]=-1
return(similarity_score)} else return(-1) }
# FUNCTION TO CALCULATE SIMILARITY VECTOR OF SPECTRUM WITH LIBRARY SPECTRA
SpecSimLib=function(spec,lib,log=F) {
sapply(1:length(lib), function(i) SpecSim(spec,lib[[i]]$spectrum,log=log)) }
# FUNCTION TO ADD BEST MATCH OF SAMPLE RECORD rec IN SPECTRAL LIBRARY lib TO ORIGINAL RECORD
# we only compare spectra when list element RI in the sample record is within tol of that in the library
# when there is a spectral match > specsimcut within a RI devation less than tol,
# we add the record nrs in the library with the best spectral matches, the spectral similarity and the RI deviation to recs
addbestlibmatch=function(rec,lib,xvar="RI",tol=10,log=F,specsimcut=0.8) {
nohit=list(record=-1,specmatch=NA,RIdev=NA)
selected=abs(sapply(lib, "[[", xvar)-rec[[xvar]])<tol
if (sum(selected)!=0) {
specsims=SpecSimLib(rec$spectrum,lib[selected],log) # HOW CAN I AVOID PASSING THE RIGHT LIBRARY SUBSET EACH TIME?
maxspecsim=max(specsims)
if (maxspecsim>specsimcut) {besthsel=which(specsims==maxspecsim)[[1]] # nr of best hit among selected elements, in case of ties we just take the 1st hit
idbesth=which(selected)[[besthsel]] # record nr of best hit in library lib
return(modifyList(rec,list(record=idbesth,specsim=specsims[[besthsel]],RIdev=rec[[xvar]]-lib[[idbesth]][[xvar]])))}
else {return(rec)} } else {return(rec)}
}
# FUNCTION TO ADD BEST LIBRARY MATCHES TO RECORDS RECS
library(pbapply)
addbestlibmatches1=function(recs,lib,xvar="RI",tol=10,log=F,specsimcut=0.8) {
pblapply(1:length(recs), function(i) addbestlibmatch(recs[[i]],lib,xvar,tol,log,specsimcut))
}
# PARALLELIZED VERSION
library(snowfall)
addbestlibmatches2=function(recs,lib,xvar="RI",tol=10,log=F,specsimcut=0.8,cores=4) {
sfInit(parallel=TRUE,cpus=cores,type="SOCK")
sfExportAll()
sfLapply(1:length(recs), function(i) addbestlibmatch(recs[[i]],lib,xvar,tol,log,specsimcut))
sfStop()
}
# TEST TIMINGS
system.time(addbestlibmatches1(s,lib))
#|++++++++++++++++++++++++++++++++++++++++++++++++++| 100%
#user system elapsed
#83.60 0.06 83.82
system.time(addbestlibmatches2(s,lib))
#user system elapsed - a bit better, but not much
#2.59 0.74 42.37 发布于 2014-07-07 17:50:25
如果不详细查看所有代码,我认为SpecSim函数仍有改进的余地,而不需要使用所有的C++。您正在使用merge,这将强制您的矩阵到data.frames。这对表演总是不利的。您的大部分代码时间可能在merge()和subsetting中。data.frame子集是慢的,矩阵或向量是快速的。
SpecSim2 <- function (ms1,ms2,log=F) {
i <- unique(c(ms1[,1], ms2[,1]))
y <- x <- numeric(length(i))
x[match(ms1[,1], i)] <- ms1[, 2]
y[match(ms2[,1], i)] <- ms2[, 2]
x <- 100 * x / max(x)
y <- 100 * y / max(y)
if (log){
x <- log2(x + 1)
y <- log2(y + 1)
}
similarity.score <- x %*% y / (sqrt(sum(x^2)) * sqrt(sum(y^2)))
if (is.na(similarity.score)){
return(-1)
} else {
return(similarity.score)
}
}以下是重写和原始的一些时间安排:
> system.time(addbestlibmatches1(s,lib))
|++++++++++++++++++++++++++++++++++++++++++++++++++| 100%
user system elapsed
4.16 0.00 4.17
> system.time(addbestlibmatches1(s,lib))
|++++++++++++++++++++++++++++++++++++++++++++++++++| 100%
user system elapsed
34.25 0.02 34.34 也许不能让你达到你所需要的速度,但比提高8倍还要好.
至于如何处理addbestlibmatches(),我认为您需要重新考虑这个问题,将其作为一个矩阵问题。与其使用列表来保存库,不如为库和示例的RI值使用向量。然后,您可以这样做您的初始屏幕:
selected <- outer(sRI, libRI, FUN = '-') < 10如果您的库是一个大矩阵(质量x谱),那么您可以对所选光谱的库进行子集,并计算出样本与库中一次选定的整个部分之间的距离,如下所示:
SpecSimMat <- function(x, lib, log = F){
stopifnot(require(matrixStats))
x <- 100 * x / max(x)
lib <- sweep(lib, 2, colMaxs(lib))
x %*% lib / (sqrt(sum(x^2)) * sqrt(colSums(lib^2)))}
其中x是样本,lib是所选光谱的矩阵(质量x谱)。通过这种方式,您可以设置一个矩阵(fast),然后执行一系列矩阵操作(fast)。
https://stackoverflow.com/questions/24607594
复制相似问题