【问题标题】:binary search with data table, include second, third, fourth,.. nearest value使用数据表进行二分查找,包括第二个、第三个、第四个、.. 最接近的值
【发布时间】:2018-04-09 14:25:50
【问题描述】:

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

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

我在这里得到了漂亮且非常快速的二进制搜索解决方案: Matching two very very large vectors with tolerance (fast! but working space sparing)

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

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

第一:当我有例如a 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]

sample 只是一个向量。但是,如果样本是一个数据框,其列 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

【问题讨论】:

  • 请一次只问一个问题。对于您的第二个问题(应该放在单独的帖子中),请提供合适的minimal reproducible example。谢谢。
  • 我添加了一个示例数据框。但如果这是更复杂的问题,我会问一个新问题。非常感谢您的回答!

标签: r performance data.table match binary-search


【解决方案1】:

你的匹配条件

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

可以改写为

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

eps = 0.5E-6.

使用它,我们可以使用 non-equi-joinreferencelist 中为每个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

假设我们修改了referencelist2F = 154.00320,那么这也会被捕获:

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)))

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

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

【讨论】:

  • 非常感谢,我一回到我的办公桌上就会尝试一下
猜你喜欢
  • 1970-01-01
  • 2015-06-02
  • 1970-01-01
  • 1970-01-01
  • 2013-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多