使用数据 table 进行二分查找,包括第二个、第三个、第四个……最接近的值

binary search with data table, include second, third, fourth,.. nearest value

假设我有两个向量。一个是参考 vector/list,它包括所有感兴趣的值和一个可能包含任何可能值的样本向量。现在我想在参考列表中找到我的样本的匹配项,具有一定的公差,该公差不固定并且依赖于向量内的比较值:

matches: abs(((referencelist - sample[i])/sample[i])*10^6)) < 0.5

舍入两个向量是没有选择的!

例如考虑:

referencelist <- read.table(header=TRUE, text="value  name
154.00312  A
154.07685  B
154.21452  C
154.49545  D
156.77310  E
156.83991  F
159.02992  G
159.65553  H
159.93843  I")

sample <- c(154.00315, 159.02991, 154.07688, 156.77312)

所以我得到了结果:

  name value      reference
1    A   154.00315  154.00312
2    G   159.02991  159.02992
3    B   154.07688  154.07685
4    E   156.77312  156.77310

我在这里得到了漂亮且非常快速的二进制搜索解决方案:

library(data.table)

dt <- as.data.table(referencelist)
setattr(dt, "sorted", "value")

tol <- 0.5
dt2 <- dt[J(sample), .(.I, ref = value, name), roll = "nearest", by = .EACHI]
dt2[, diff := abs(ref - value) / value * 1e6]
dt2[diff <= tol]

#       value I      ref name       diff
# 1: 154.0032 1 154.0031    A 0.19480121
# 2: 159.0299 7 159.0299    G 0.06288125
# 3: 154.0769 2 154.0769    B 0.19470799
# 4: 156.7731 5 156.7731    E 0.12757289

但是这里出现了其他问题,我真的不知道如何继续,很乐意提供任何进一步的帮助:

首先: 当我有例如参考列表中的 F = 154.0033。然后我的样本值154.0032不仅在上面A的公差范围内,而且在F的范围内。然而data.table方法只给我最接近的值。我怎样才能获得第二个、第三个……等等最接近的值,但仍然使用 data.table,因为这是对我的大型数据集足够快的唯一解决方案。如果有可能获得行号,则可能只是从参考列表中最近匹配的行开始 +-x 以获得公差范围内的所有可能值,因为它是有序的。那么有没有像

dt2 <- dt[J(sample), .(.I, ref = value, name), roll = "nearest" +-x , by = .EACHI]

?

第二:当使用

dt2 <- dt[J(sample), .(.I, ref = value, name), roll = "nearest", by = .EACHI]

样本只是一个向量。但是,如果样本是一个数据框,其列 value 是与引用列表匹配的关键,但还有其他 100 多列应保留在结果 data.table 中,该怎么办?我真的试图理解 data.tables 的语法,但没有成功。也有人可以帮助我吗?

这里举个例子:

sample <- data.frame(value=c(154.00315, 159.02991, 154.07688, 156.77312),replicate(100,sample(0:1,4,rep=TRUE)))

例如得到这样的东西:

      value   I   ref     name  diff      X1 ... X100
# 1: 154.0032 1 154.0031    A  0.19480121  X       X
# 2: 159.0299 7 159.0299    G  0.06288125  X       X
# 3: 154.0769 2 154.0769    B  0.19470799  X       X
# 4: 156.7731 5 156.7731    E  0.12757289  X       X

你的匹配条件

abs(((referencelist - sample[i])/sample[i])*10^6)) < 0.5

可以重写为

sample[i] * (1 - eps) < referencelist < sample[i] * (1 + eps)

eps = 0.5E-6.

使用它,我们可以使用 非等值连接 为每个 referencelist 找到 all 个匹配项 sample:

library(data.table)
options(digits = 10)
eps <- 0.5E-6 # tol * 1E6
setDT(referencelist)[.(value = sample, 
                       lower = sample * (1 - eps), 
                       upper = sample * (1 + eps)), 
                     on = .(ref > lower, ref < upper), .(name, value, reference = x.ref)]

重现预期结果:

   name     value reference
1:    A 154.00315 154.00312
2:    G 159.02991 159.02992
3:    B 154.07688 154.07685
4:    E 156.77312 156.77310

比方说,我们用 F = 154.00320 修改了 referencelist2 那么这也会被捕获:

setDT(referencelist2)[.(value = sample, 
                       lower = sample * (1 - eps), 
                       upper = sample * (1 + eps)), 
                     on = .(ref > lower, ref < upper), .(name, value, reference = x.ref)]
   name     value reference
1:    A 154.00315 154.00312
2:    F 154.00315 154.00320
3:    G 159.02991 159.02992
4:    B 154.07688 154.07685
5:    E 156.77312 156.77310

顺便说一下,表达式
.(value = sample, lower = sample * (1 - eps), upper = sample * (1 + eps))
正在动态创建 data.table。我们可以在这里加入另一个 datat.table。

编辑:sample 作为数据框给出

为了说明他问题的第二部分,OP 给出了 sample 作为具有 100 多列的数据框:

sample <- data.frame(value = c(154.00315, 159.02991, 154.07688, 156.77312),
                     replicate(100L, sample(0:1, 4L, rep = TRUE)))

这也可以通过非等连接来处理,但需要进行一些修改:

eps <- 0.5E-6 # tol * 1E6
setDT(referencelist2)[
  # modify referencelist2 by copying ref column to preserve its value
  , reference := ref][
    # modify sample by appending lower and upper for non-equi join
    setDT(sample)[, c("lower", "upper") := .(value * (1 - eps), value * (1 + eps))], 
    on = .(ref > lower, ref < upper)][
      # remove upper and lower columns (renamed to ref and ref.1 during the join)
      , -c("ref", "ref.1")]
   name reference     value X1 X2 X3 X4 X5 X6 X7 X8 X9 X10 X11 X12 X13 X14 X15 X16 ...
1:    A 154.00312 154.00315  0  1  1  0  0  0  0  1  0   0   0   1   0   0   0   0   0
2:    F 154.00320 154.00315  0  1  1  0  0  0  0  1  0   0   0   1   0   0   0   0   0
3:    G 159.02992 159.02991  0  0  0  1  0  1  1  0  0   0   1   1   1   0   1   1   0
4:    B 154.07685 154.07688  0  1  1  1  1  1  1  1  1   1   0   1   1   1   0   0   0
5:    E 156.77310 156.77312  1  0  1  0  1  0  0  1  1   1   0   1   0   1   0   1   0