data.table roll =“nearest”返回多个结果

时间:2014-02-19 15:39:17

标签: r data.table

我正在尝试使用data.table来匹配向量中最接近的十进制值,但是遇到了返回多个结果的情况。下面的简化示例返回两个值0.1818182 0.2727273,但使用x的不太精确的值(例如0.0275)会返回单个匹配项(0.1818182)。

x = 0.0275016249293408
dt = data.table(rnk = c(0, 0.0909090909090909, 
                        0.181818181818182, 0.272727272727273),
                val = c(0.0233775088495975, 0.0270831481152598, 
                        0.0275016216267234, 0.0275016249293408),
                key="val")
dt[J(x), roll="nearest"][, ifelse(is.na(val), NA_real_, rnk)]

我假设问题与我用于此比较的数值的精度有关。可以用于最近匹配的小数精度是否有限制(即我需要舍入数据点)?有没有更好的方法来完成这个最接近的比赛?

2 个答案:

答案 0 :(得分:9)

是的,data.table在加入numeric列时会自动应用容差。 v1.8.10中的容差为sqrt(.Machine$double.eps) == 1.490116e-08。这直接来自?base::all.equal

为了说明,请考虑分组:

> dt
          rnk        val
1: 0.00000000 0.02337751
2: 0.09090909 0.02708315
3: 0.18181818 0.02750162
4: 0.27272727 0.02750162

> dt[,.N,by=val]
          val N
1: 0.02337751 1
2: 0.02708315 1
3: 0.02750162 2    # one group, size two
>

当您使用dt[J(x), roll="nearest"]加入时,x值匹配在容差范围内,并且您获得了与之匹配的组,就像通常在滚动连接中出现匹配值一样。 roll="nearest"仅适用于不匹配的值,超出容差范围。

data.table认为val的第3行和第4行中的值相等。这背后的想法是为了方便,因为大多数时候键值实际上是固定的精度,例如价格(1.23美元)或记录的测量到指定的精度(1.234567)。我们希望加入并对这样的numerics进行分组,即使在它们相乘之后,也不需要自己编码机器精度。我们希望在numeric数据显示为表格中相同时避免混淆,但由于位表示中的微小差异而不是。

有关此示例,请参阅?unique.data.table

DT = data.table(a=tan(pi*(1/4 + 1:10)), b=rep(1,10))   # example from ?all.equal
length(unique(DT$a))         # 10 strictly unique floating point values
all.equal(DT$a,rep(1,10))    # TRUE, all within tolerance of 1.0
DT[,which.min(a)]            # row 10, the strictly smallest floating point value
identical(unique(DT),DT[1])  # TRUE, stable within tolerance
identical(unique(DT),DT[10]) # FALSE

data.table在容忍范围内稳定;即,当您按numeric分组时,该组中项目的原始顺序将照常维护。

> dt$val[3] < dt$val[4]   # in your example data, 3 is strictly less than 4
[1] TRUE
> dt[, row:=1:4]  # add a row number to illustrate
> dt[, list(.N, list(row)), by=val]
          val N  V2
1: 0.02337751 1   1
2: 0.02708315 1   2
3: 0.02750162 2 3,4
> dt[3:4, val:=rev(val)]   # swap the two values around
> dt$val[3] > dt$val[4]
[1] TRUE
> dt[, list(.N, list(row)), by=val]
          val N  V2
1: 0.02337751 1   1
2: 0.02708315 1   2
3: 0.02750162 2 3,4    # same result, consistent. stable within tolerance

答案 1 :(得分:5)

参考Matt的答案,有一种简单的方法可以使用双重提供的所有15位有效数字,以便正确选择最接近的匹配行。可以将值向上扩展以确保15个有效数字位于10 ^( - 8)级别之上,而不是处理原始值。这可以按如下方式完成:

orig_vals <- dt[,val]
scale_fact <- max(10^(trunc(log10(abs(orig_vals)))+8))
scaled_vals <- orig_vals * scale_fact
dt[,scaled_val:=scaled_vals]
setkey(dt,scaled_val)

现在,执行滚动连接

scaled_x <- x*scale_fact
dt[J(scaled_x), roll="nearest"][, ifelse(is.na(val), NA_real_, rnk)]

# [1] 0.2727273

产量 - 根据需要 - 单个值。

如果在两个相同的键值的情况下也只应选择一行,则可以将mult="first"参数添加到上述data.table调用中。