检查向量中的每个数字是否在 R 中另一个向量中的某些数字之间

Check if each number in a vector is between some numbers in another vector in R

假设我有两个向量,A 和 B。A 有 15 个变量,B 有 28 个变量。

A = c(13,14,29,31,32,39,42,51,59,61,68,91,102,109,120)

B = c(26,26,28,29,30,30,33,38,41,42,45,46,47,47,49,49,80,81,86,86,90,90,92,100,101,105,105,107)

我想要一个 14 x 27 矩阵 Z,如果 (B_j,B_{j+1}] 与 (A_i, A_{i+ 1}].

例如,Z 的 (3,4) 条目将是 1,因为 (29,31] 和 (29,30] 重叠,30 作为公共数字。有没有快速的计算方法?

我有以下代码:

Z = matrix(0, length(A)-1, length(B)-1)
for (i in 1:(length(A)-1)){
  nn = which(B > A[i] & B <= A[(i+1)])
  if (length(nn)>0){
    Z[i,(nn-1)] = 1}}

它运行良好,但我的 A 和 B 向量通常包含 30,000 多个元素,而且速度非常慢。制作矩阵 Z 甚至会花费不必要的长时间。有人可以帮忙吗?

理想情况下,有一个向量化的解决方案来解决这个问题,或者有一个编写良好的函数可以像切蛋糕一样做这件事。

这是一个使用矩阵乘法的选项。正如所评论的那样,矩阵可能会变大,您必须看看速度改进是否值得:

res1 <- outer(A, B, FUN = function(A, B){B > A})
res2 <- outer(A, B, FUN = function(A, B){B <= A})
dim(res1); dim(res2)
res3 <- (res1[-nrow(res1),] + res2[-1,]) == 2
image(res3)
dim(res3)

op <- par(mfcol=c(1,2))
image(Z, main="Z")
image(res3, main="res3")
par(op)

这个答案使用矩阵索引并依赖于 expand.grid 尽管它有更快的实现。您滞后向量以创建 A 和 B 的矩阵,然后使用一个执行简单布尔检查的函数,我们可以使用扩展网格对矩阵进行索引。然后它returns一个矩阵。

overlap = function(id,x1,x2){
  idA = id[,1]
  idB = id[,2]
  o = (x1[idA,1] >= x2[idB,1] & x1[idA,1] <= x2[idB,2]) | (x1[idA,2] >= x2[idB,1] & x1[idA,2] <= x2[idB,2]) |
    (x1[idA,1] <= x2[idB,1] & x1[idA,2] >= x2[idB,1]) | (x1[idA,1] <= x2[idB,2] & x1[idA,2] >= x2[idB,2])
  matrix(o,nrow=nrow(x1))
}

A = c(13,14,29,31,32,39,42,51,59,61,68,91,102,109,120)
nA = cbind(lag(A),A)[-1,]

B = c(26,26,28,29,30,30,33,38,41,42,45,46,47,47,49,49,80,81,86,86,90,90,92,100,101,105,105,107)
nB = cbind(lag(B),B)[-1,]

expand.grid.jc <- function(seq1,seq2) {
  cbind(Var1 = rep.int(seq1, length(seq2)), 
    Var2 = rep.int(seq2, rep.int(length(seq1),length(seq2))))
}

ids = expand.grid.jc(1:nrow(nA),1:nrow(nB))
overlap(ids,nA,nB)

如果闭区间 [B_j,B_{j+1}][A_i, A_{i+1}] 也适合您,您可以使用

A <- as.integer(c(13,14,29,31,32,39,42,51,59,61,68,91,102,109,120))
B <- as.integer(c(26,26,28,29,30,30,33,38,41,42,45,46,47,47,49,49,80,81,86,86,90,90,92,100,101,105,105,107))

DT_A <- data.table(A0 = A, A1 = shift(A, type = "lead"), key=c("A0", "A1"))[-length(A)]
DT_B <- data.table(B0 = B, B1 = shift(B, type = "lead"), key=c("B0", "B1"))[-length(B)]

ind_true <- foverlaps(DT_A, DT_B, type="any", mult="all", which=TRUE)[!is.na(yid)]

mat <- matrix(0, length(A)-1, length(B)-1)
mat[ind_true$xid, ind_true$yid] = 1