簡體   English   中英

R:根據自定義距離功能和多個條件快速匹配記錄

[英]R: quickly match records based on custom distance function and multiple criteria

我在R中做了一些功能,以基於自定義的光譜相似度函數和化合物的所謂保留指數(即化合物)的匹配,將化學質譜圖(一個具有兩列質量和強度均是整數的矩陣)與此類光譜庫進行匹配(洗脫時間)(請參見此處的示例, http: //webbook.nist.gov/cgi/cbook.cgi?ID = C630035&Mask = 200 )。 為此,必須將每個記錄的列表元素“ RI”與庫中的列表元素進行比較,並且當絕對偏差小於給定的公差時,應將最佳光譜庫匹配項添加到我的記錄中。 下面是我為此編寫的一些代碼,但問題是它對於我的目的而言太慢了(我通常有大約1000個樣品光譜和20萬個庫光譜)。 我嘗試並行化它,但這似乎並沒有太大幫助。 關於如何使下面的代碼更高效(例如使用更多的矢量化或使用內聯C代碼或其他一些R技巧),您可能有任何想法嗎? 我知道這方面的一般建議,但是在這種情況下卻不太清楚如何輕松地實現它(而且不幸的是,我對C的了解還不夠熟練)...有什么想法或建議嗎? 哦,是的,使用sfLapply時如何添加進度欄? 可能有助於將我的光譜首先放在一個大的(稀疏的,因為有很多零)矩陣中,以避免光譜相似性函數中的merge步驟,或者使用其他標准,例如僅當最大/查詢光譜中最強的峰與庫光譜具有相同的質量(或包含在例如庫光譜中的5個最大峰集中)? 無論如何,對於如何加快此任務的任何想法將不勝感激!

編輯:我仍然有一個剩余查詢是如何避免在函數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 

在不詳細查看所有代碼的情況下,我認為在沒有使用所有C ++的情況下,SpecSim函數仍有改進的余地。 您正在使用合並,將矩陣強制轉換為data.frames。 這總是對性能不利。 您的大部分代碼時間可能都在merge()和子設置中。 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光譜)。 這樣,您就可以為矩陣設置子集(快速),然后執行一系列矩陣操作(快速)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM