与间隔匹配并提取两个矩阵 R 之间的值

match with an interval and extract values between two matrix R

我在列表中有 n 个矩阵和一个附加矩阵,其中包含我想在矩阵列表中找到的值。

要获取矩阵列表,我使用以下代码:

setwd("C:\~\Documents\R") 


import.multiple.txt.files<-function(pattern=".txt",header=T)
{
list.1<-list.files(pattern=".txt")
list.2<-list()
for (i in 1:length(list.1))
{
list.2[[i]]<-read.delim(list.1[i])
}
names(list.2)<-list.1
list.2

}


txt.import.matrix<-cbind(txt.import)

我的列表是这样的:(我只展示了一个 n=2 的例子)。每个数组中的行数不同(这里我只取5行和6行来简化,但我的真实数据中有500多行)。

txt.import.matrix[1]

    [[1]]
     X.     RT.     Area.  m.z.      
1     1     1.01   2820.1  358.9777  
2     2     1.03   9571.8  368.4238  
3     3     2.03   6674.0  284.3294  
4     4     2.03   5856.3  922.0094  
5     5     3.03   27814.6 261.1299  


txt.import.matrix[2]

    [[2]]
     X.     RT.     Area.  m.z.      
1     1     1.01    7820.1 358.9777  
2     2     1.06    8271.8 368.4238  
3     3     2.03   12674.0 284.3294  
4     4     2.03    5856.6 922.0096  
5     5     2.03   17814.6 261.1299
6     6     3.65    5546.5 528.6475  

我想在矩阵列表中找到另一个值数组。该数组是通过将列表中的所有数组合并到一个数组中并删除重复项获得的。

reduced.list.pre.filtering

     RT.   m.z.
1    1.01  358.9777
2    1.07  368.4238
3    2.05  284.3295
4    2.03  922.0092
5    3.03  261.1299
6    3.56  869.4558

我想获得一个新矩阵,其中写入了列表中所有矩阵的匹配 RT. ± 0.02m.z. ± 0.0002Area. 结果。输出可能是这样的。

     RT.   m.z.        Area.[1]      Area.[2]
1    1.01  358.9777    2820.1        7820.1
2    1.07  368.4238                  8271.8      
3    2.05  284.3295    6674.0        12674.0
4    2.03  922.0092    5856.3             
5    3.03  261.1299    27814.6            
6    3.65  528.6475    

我只知道如何在一个数组中只匹配一个精确值。这里的难点是在数组列表中找到值,需要找到值±一个区间。如果您有任何建议,我将不胜感激。

这是一个快速粗略的方法,如果我明白你想要做什么,可能会有所帮助。

取消列出两个矩阵的每个变量的值

areas <- unlist(lapply(txt.import.matrix, function(x) x$Area.))
rts <- unlist(lapply(txt.import.matrix, function(x) x$RT.))
mzs <- unlist(lapply(txt.import.matrix, function(x) x$m.z.))

找到 RT 和 m.z 的那些值的索引。最接近第三个值的 matrix/df:

 rtmins <- lapply(reduced.list.pre.filtering$RT., function(x) which(abs(rts-x)==min(abs(rts-x))))
mzmins <- lapply(reduced.list.pre.filtering$m.z., function(x) which(abs(mzs-x)==min(abs(mzs-x))))

使用purrr快速计算出两者中有哪些索引(即每个索引的最小差异):

inboth <- purrr::map2(rtmins,mzmins,intersect)

获取对应的面积值:

vals<-lapply(inboth, function(x) areas[x])

使用reshape2输入宽幅:

vals2 <- reshape2::melt(vals)
vals2$number <- ave(vals2$L1, vals2$L1, FUN = seq_along)
vals.wide <-reshape2::dcast(vals2, L1 ~ number, value.var="value")

cbind(reduced.list.pre.filtering, vals.wide)

#   RT.     m.z. L1       1       2
#1 1.01 358.9777  1  2820.1  7820.1
#2 1.07 368.4238  2  8271.8      NA
#3 2.05 284.3295  3  6674.0 12674.0
#4 2.03 922.0092  4  5856.3      NA
#5 3.03 261.1299  5 27814.6      NA

这可能会给您一些想法。可以轻松调整以检查共享最小值是否超过 +/- 值。

使用 data.table 当前开发版本 v1.9.7 中的 non-equi 连接(请参阅 installation instructions),它允许为连接提供非相等条件:

require(data.table) # v1.9.7
names(ll) = c("Area1", "Area2")
A = rbindlist(lapply(ll, as.data.table), idcol = "id")           ## (1)

B = as.data.table(mat)
B[, c("RT.minus", "RT.plus") := .(RT.-0.02, RT.+0.02)]
B[, c("m.z.minus", "m.z.plus") := .(m.z.-0.0002, m.z.+0.0002)]   ## (2)

ans = A[B, .(id, X., RT. = i.RT., m.z. = i.m.z., Area.), 
           on = .(RT. >= RT.minus, RT. <= RT.plus, 
                  m.z. >= m.z.minus, m.z. <= m.z.plus)]          ## (3)

dcast(ans, RT. + m.z. ~ id)                                      ## (4)
# or dcast(ans, RT. + m.z. ~ id, fill = 0)
#     RT.     m.z.   Area1   Area2
# 1: 1.01 358.9777  2820.1  7820.1
# 2: 1.07 368.4238      NA  8271.8
# 3: 2.03 922.0092  5856.3      NA
# 4: 2.05 284.3295  6674.0 12674.0
# 5: 3.03 261.1299 27814.6      NA

[1] 命名矩阵列表(此处称为 ll)并使用 lapply() 将每个矩阵转换为 data.table ,并使用 rbindlist 按行绑定它们,并将名称添加为额外的列 (idcol)。称之为 A.

[2] 将第二个矩阵(此处称为 mat)也转换为 data.table。添加与要搜索的 ranges/intervals 相对应的其他列(因为 on= 参数,正如我们将在下一步中看到的,无法处理表达式 yet).称之为 B.

[3] 执行条件 join/subset。对于 B 中的每一行,在 A 中找到与提供给 on= 参数的条件对应的匹配行,并提取这些匹配行索引的列 id, X., R.T. and m.z.

[4]留在[3]比较好。但是,如果您希望它如您的答案所示,我们可以将其重塑为宽格式。 fill = 0 会将结果中的 NAs 替换为 0.

这是 Arun 使用 data.table 相当优雅的回答的另一种方法。我决定 post 它是因为它包含两个额外的方面,这两个方面在您的问题中是重要的考虑因素:

  1. 浮点数比较:比较浮点值是否在区间内需要考虑计算区间时的舍入误差。这是比较实数的浮点表示的一般问题。请参阅 R 上下文中的 this and this。以下在函数 in.interval.

  2. 中实现此比较
  3. 多个匹配项: 如果间隔重叠,您的间隔匹配条件可能会导致多个匹配项。以下 假设 您只需要第一个匹配项(关于每个 txt.import.matrix 矩阵的增加行)。这是在函数 match.interval 中实现的,并在后面的注释中进行了解释。如果您想获得符合您标准的区域的平均值,则需要其他逻辑。

为了在矩阵 txt.import.matrix 中为矩阵 reduced.list.pre.filtering 中的每一行找到匹配行,以下代码将比较函数在 [=240= 上的应用向量化] reduced.list.pre.filtering 和来自 txt.import.matrix 的矩阵之间的所有枚举行对。从功能上讲,对于此应用程序,这与 Arun 使用 data.tablenon-equi 连接的解决方案相同;然而,non-equi 连接功能更通用,而且 data.table 实现很可能针对内存使用和速度进行了更好的优化,即使对于此应用程序也是如此。

in.interval <- function(x, center, deviation, tol = .Machine$double.eps^0.5) {
  return (abs(x-center) <= (deviation + tol))
}

match.interval <- function(r, t) {
  r.rt <- rep(r[,1], each=nrow(t))
  t.rt <- rep(t[,2], times=nrow(r))
  r.mz <- rep(r[,2], each=nrow(t))
  t.mz <- rep(t[,4], times=nrow(r))                                       ## 1.

  ind <- which(in.interval(r.rt, t.rt, 0.02) & 
               in.interval(r.mz, t.mz, 0.0002))
  r.ind <- floor((ind - 1)/nrow(t)) + 1                                   ## 2.

  dup <- duplicated(r.ind)
  r.ind <- r.ind[!dup]
  t.ind <- ind[!dup] - (r.ind - 1)*nrow(t)                                ## 3.
  return(cbind(r.ind,t.ind))                       
}

get.area.matched <- function(r, t) {
  match.ind <- match.interval(r, t)
  area <- rep(NA,nrow(r))
  area[match.ind[,1]] <- t[match.ind[,2], 3]                              ## 4.
  return(area)
}

res <- cbind(reduced.list.pre.filtering,
             do.call(cbind,lapply(txt.import.matrix, 
                                  get.area.matched, 
                                  r=reduced.list.pre.filtering)))         ## 5.
colnames(res) <- c(colnames(reduced.list.pre.filtering), 
                   sapply(seq_len(length(txt.import.matrix)), 
                          function(i) {return(paste0("Area.[",i,"]"))}))  ## 6.
print(res)
##      RT.     m.z. Area.[1] Area.[2]
##[1,] 1.01 358.9777   2820.1   7820.1
##[2,] 1.07 368.4238       NA   8271.8
##[3,] 2.05 284.3295   6674.0  12674.0
##[4,] 2.03 922.0092   5856.3       NA
##[5,] 3.03 261.1299  27814.6       NA
##[6,] 3.56 869.4558       NA       NA

备注:

  1. 这部分构造数据以实现比较函数在 reduced.list.pre.filtering 和矩阵 space 之间的所有枚举行对的应用的矢量化=19=]。要构造的数据是四个数组,它们是比较标准中使用的两列的复制(或扩展) reduced.list.pre.filtering 每个矩阵的行维度来自 txt.import.matrix 和复制reduced.list.pre.filtering 行维度中 txt.import.matrix 中每个矩阵的两列,用于比较标准。这里,术语数组指的是二维矩阵或一维向量。得到的四个数组是:

    • r.rtreduced.list.pre.filteringRT.列(即r[,1])在t[=221=的行维度中的复制]
    • t.rtrtxt.import.matrix 矩阵的 RT. 列的复制(即 t[,2]r
    • r.mzreduced.list.pre.filteringm.z. 列(即 r[,2])在 t
    • 的行维度中的复制
    • t.mzr[=行维度中来自txt.import.matrix(即t[,4])矩阵的m.z.列的复制221=]

    重要的是,每个数组的索引都以相同的方式枚举了 rt 中的所有行对。具体来说,将这些数组视为大小为 M by N 的二维矩阵,其中 M=nrow(t)N=nrow(r),行索引对应于 t 和列索引对应于 r 的行。因此,第 i 行和第 j 列(四个数组中的每一个)的数组值(在所有四个数组中)是 j - r 的第行和 i - t 的行。此复制过程的实现使用 R 函数 rep。例如,在计算 r.rt 时,使用 repeach=M,其效果是将其数组输入 r[,1] 视为行向量并复制该行 M 次形成 M 行。结果是,对应于 r 中的一行的每一列都具有来自 r 对应行的 RT. 值,并且该值对于所有行(属于r.rt 的列),每个对应 t 中的一行。这意味着在将 r 中的该行与 t 中的任何行进行比较时,使用 r 中该行的 RT. 的值。相反,在计算 t.rt 时,使用 reptimes=N,其作用是将其数组输入视为列向量并将该列复制 N 次以形成一个N 列。结果是 t.rt 中的每一行对应于 t 中的一行,具有来自 t 对应行的 RT. 值,并且该值相同对于 t.rt 的所有(该行的)列,每一列对应于 r 中的一行。这意味着在将 t 中的该行与 r 中的任何行进行比较时,使用 t 中该行的 RT. 的值。同样,r.mzt.mz 的计算分别使用 rt 中的 m.z. 列。

  2. 这会执行向量化比较,生成 M by N 逻辑矩阵,其中第 i 行和第 j 行如果 r 的第 j 行与 t 的第 i 行匹配标准,则列为 TRUE,否则为 FALSE . which()的输出是这个逻辑比较结果矩阵的数组索引数组,其中它的元素是TRUE。我们想将这些数组索引转换为比较结果矩阵的行和列索引,以引用回 rt 的行。下一行从数组索引中提取列索引。请注意,变量名称是 r.ind,表示这些对应于 r 的行。我们首先提取它,因为它对于检测 r.

  3. 中一行的多个匹配很重要
  4. 这部分处理 r 中给定行的 t 中可能的多个匹配项。多个匹配项将在 r.ind 中显示为重复值。如上所述,这里的逻辑只保留 t 中增加行的第一个匹配项。函数duplicated returns 数组中所有重复值的索引。因此,删除这些元素将达到我们想要的效果。代码首先从r.ind中移除它们,然后从ind中移除它们,最后计算列索引到比较结果矩阵,它对应于t的行,使用修剪indr.indmatch.interval 返回的是一个矩阵,其行是匹配的行索引对,其第一列是 r 的行索引,第二列是 t.[=164 的行索引=]

  5. get.area.matched 函数只是使用 match.ind 的结果从 t 中提取所有匹配项的 Area。请注意,返回的结果是一个(列)向量,长度等于 r 中的行数并初始化为 NA。通过这种方式,r 中在 t 中没有匹配的行返回了 AreaNA

  6. 这使用 lapply 在列表 txt.import.matrix 上应用函数 get.area.matched 并将返回的匹配 Area 结果附加到 reduced.list.pre.filtering 作为列向量。同样,相应的列名也被附加并设置在结果 res.

编辑: 使用 foreach 包的替代实现

事后看来,更好的实现方式是使用 foreach 包对比较进行矢量化。在此实现中,需要 foreachmagrittr

require("magrittr")  ## for %>%
require("foreach")

然后match.interval中的代码用于向量化比较

r.rt <- rep(r[,1], each=nrow(t))
t.rt <- rep(t[,2], times=nrow(r))
r.mz <- rep(r[,2], each=nrow(t))
t.mz <- rep(t[,4], times=nrow(r))                       # 1.

ind <- which(in.interval(r.rt, t.rt, 0.02) & 
             in.interval(r.mz, t.mz, 0.0002))

可以替换为

ind <- foreach(r.row = 1:nrow(r), .combine=cbind) %:% 
         foreach(t.row = 1:nrow(t)) %do% 
           match.criterion(r.row, t.row, r, t) %>% 
             as.logical(.) %>% which(.)

其中 match.criterion 定义为

match.criterion <- function(r.row, t.row, r, t) {
  return(in.interval(r[r.row,1], t[t.row,2], 0.02) & 
         in.interval(r[r.row,2], t[t.row,4], 0.0002))
}

这更容易解析并反映正在执行的操作。请注意,嵌套的 foreachcbind 组合返回的又是一个逻辑矩阵。最后,get.area.matched 函数在列表 txt.import.matrix 上的应用也可以使用 foreach:

执行
res <- foreach(i = 1:length(txt.import.matrix), .combine=cbind) %do% 
         get.area.matched(reduced.list.pre.filtering, txt.import.matrix[[i]]) %>%
           cbind(reduced.list.pre.filtering,.)

使用foreach的完整代码如下:

require("magrittr")
require("foreach")

in.interval <- function(x, center, deviation, tol = .Machine$double.eps^0.5) {
  return (abs(x-center) <= (deviation + tol))
}

match.criterion <- function(r.row, t.row, r, t) {
  return(in.interval(r[r.row,1], t[t.row,2], 0.02) & 
     in.interval(r[r.row,2], t[t.row,4], 0.0002))
}

match.interval <- function(r, t) {
  ind <- foreach(r.row = 1:nrow(r), .combine=cbind) %:% 
       foreach(t.row = 1:nrow(t)) %do% 
     match.criterion(r.row, t.row, r, t) %>% 
       as.logical(.) %>% which(.)
  # which returns 1-D indices (row-major),
  # convert these to 2-D indices in (row,col)
  r.ind <- floor((ind - 1)/nrow(t)) + 1                   ## 2.
  # detect duplicates in r.ind and remove them from ind
  dup <- duplicated(r.ind)
  r.ind <- r.ind[!dup]
  t.ind <- ind[!dup] - (r.ind - 1)*nrow(t)                ## 3.

  return(cbind(r.ind,t.ind))                       
}

get.area.matched <- function(r, t) {
  match.ind <- match.interval(r, t)
  area <- rep(NA,nrow(r))
  area[match.ind[,1]] <- t[match.ind[,2], 3]
  return(area)
}

res <- foreach(i = 1:length(txt.import.matrix), .combine=cbind) %do% 
     get.area.matched(reduced.list.pre.filtering, txt.import.matrix[[i]]) %>%
       cbind(reduced.list.pre.filtering,.)

colnames(res) <- c(colnames(reduced.list.pre.filtering), 
           sapply(seq_len(length(txt.import.matrix)), 
              function(i) {return(paste0("Area.[",i,"]"))}))

希望对您有所帮助。