两个数字向量上的全对全 setdiff 具有用于接受匹配的数字阈值

viv*_*oru 5 compare r vector set-difference

我想要做的或多或少是以下两个线程中讨论的问题的组合:

我有两个数字向量:

b_1 <- c(543.4591, 489.36325, 12.03, 896.158, 1002.5698, 301.569)
b_2 <- c(22.12, 53, 12.02, 543.4891, 5666.31, 100.1, 896.131, 489.37)
Run Code Online (Sandbox Code Playgroud)

我想将所有元素b_1与所有元素进行比较b_2,反之亦然。

如果element_ib_1等于任意数量的范围 element_j ± 0.045b_2element_i必须报告。

同样地,如果element_jb_2NOT等于任何数目的范围 element_i ± 0.045b_1element_j必须报告。

因此,基于上面提供的向量的示例答案将是:

### based on threshold = 0.045
in_b1_not_in_b2 <- c(1002.5698, 301.569)
in_b2_not_in_b1 <- c(22.12, 53, 5666.31, 100.1)
Run Code Online (Sandbox Code Playgroud)

有没有 R 函数可以做到这一点?

Hen*_*rik 2

如果你乐意使用非base包,data.table::inrange是一个方便的功能。

x1[!inrange(x1, x2 - 0.045, x2 + 0.045)]
# [1] 1002.570  301.569

x2[!inrange(x2, x1 - 0.045, x1 + 0.045)]
# [1]   22.12   53.00 5666.31  100.10
Run Code Online (Sandbox Code Playgroud)

inrange在更大的数据集上也很有效。在例如1e5向量上,比其他两个替代方案快几倍inrange> 700

n <- 1e5
b1 <- runif(n, 0, 10000)
b2 <- b1 + runif(n, -1, 1)

microbenchmark(
  f1 = f(b1, b2, 0.045, 5000),
  f2 = list(in_b1_not_in_b2 = b1[sapply(b1, function(x) !any(abs(x - b2) <= 0.045))],
       in_b2_not_in_b1 = b2[sapply(b2, function(x) !any(abs(x - b1) <= 0.045))]),
  f3 = list(in_b1_not_in_b2 = b1[!inrange(b1, b2 - 0.045, b2 + 0.045)],
       in_b2_not_in_b1 = b2[!inrange(b2, b1 - 0.045, b1 + 0.045)]),
  unit = "relative", times = 10)
# Unit: relative
#  expr      min       lq     mean   median        uq       max neval
#    f1 1976.931 1481.324 1269.393 1103.567 1173.3017 1060.2435    10
#    f2 1347.114 1027.682  858.908  766.773  754.7606  700.0702    10
#    f3    1.000    1.000    1.000    1.000    1.0000    1.0000    10
Run Code Online (Sandbox Code Playgroud)

是的,他们给出了相同的结果:

n <- 100
b1 <- runif(n, 0, 10000)
b2 <- b1 + runif(n, -1, 1)

all.equal(f(b1, b2, 0.045, 5000),
          list(in_b1_not_in_b2 = b1[sapply(b1, function(x) !any(abs(x - b2) <= 0.045))],
               in_b2_not_in_b1 = b2[sapply(b2, function(x) !any(abs(x - b1) <= 0.045))]))
# TRUE

all.equal(f(b1, b2, 0.045, 5000),
          list(in_b1_not_in_b2 = b1[!inrange(b1, b2 - 0.045, b2 + 0.045)],
               in_b2_not_in_b1 = b2[!inrange(b2, b1 - 0.045, b1 + 0.045)]))
# TRUE
Run Code Online (Sandbox Code Playgroud)

在 SO 上搜索inrange时有几个相关的、可能有用的答案。