我正在尝试使用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)]
我假设问题与我用于此比较的数值的精度有关。可以用于最近匹配的小数精度是否有限制(即我需要舍入数据点)?有没有更好的方法来完成这个最接近的比赛?
答案 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
调用中。