我试图使用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)]
我假设这个问题与我用于比较的数值的精度有关。可用于最接近匹配的小数精度是否有限制(即,我需要四舍五入数据点吗)?有没有更好的方法来完成这场势均力敌的比赛?
是的,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"
仅适用于不匹配、超出公差范围的值。
CCD_ 13认为CCD_ 14的第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
CCD_ 18在耐受范围内也稳定;即,当您按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
参考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
调用中。