我最近将data.table从1.8.10升级到1.9.2,并且在跨大整数分组时我发现两个版本之间存在以下差异.
是否有一个我需要在1.9.2中更改的设置,让以下两个组语句中的第一个像在1.8.10中那样工作(我认为1.8.10是正确的行为)?
另外,对于以下两个组语句中的第二个,两个包中的结果是相同的,但是预期的行为是什么?
1.8.10
> library(data.table)
data.table 1.8.10 For help type: help("data.table")
> foo = data.table(i = c(2884199399609098249, 2884199399608934409))
> lapply(foo, class)
$i
[1] "numeric"
> foo
i
1: 2884199399609098240
2: 2884199399608934400
> foo[, .N, by=i]
i N
1: 2884199399609098240 1
2: 2884199399608934400 1
> foo = data.table(i = c(9999999999999999999, 9999999999999999998))
> foo[, .N, by=i]
i N
1: 10000000000000000000 2
>
Run Code Online (Sandbox Code Playgroud)
并且1.9.2
> library(data.table)
data.table 1.9.2 For help type: help("data.table")
> foo = data.table(i = c(2884199399609098249, 2884199399608934409))
> lapply(foo, class)
$i
[1] "numeric"
> foo
i
1: 2884199399609098240
2: 2884199399608934400
> foo[, .N, by=i]
i N
1: 2884199399609098240 2
> foo = data.table(i = c(9999999999999999999, 9999999999999999998))
> foo[, .N, by=i]
i N
1: 10000000000000000000 2
>
Run Code Online (Sandbox Code Playgroud)
用于第一次测试的数字(显示data.table版本之间的差异)是来自我的实际数据集的数字,以及导致我的一些回归测试在升级data.table后失败的数字.
我对第二次测试感到好奇,在我将数字增加了一个数量级之后,如果在data.table包的两个版本中都预期它会忽略最后一个有效数字的微小差异.
我假设这一切都与浮点表示有关.也许我处理这个问题的正确方法是将这些大整数表示为整数64或字符?我不喜欢做integer64,因为我不确定data.table和R环境是否完全支持它们,例如,我必须在之前的data.table代码中添加它:
options(datatable.integer64="character") # Until integer64 setkey is implemented
Run Code Online (Sandbox Code Playgroud)
也许这已经实现了,但无论如何更改该设置都不会改变这些测试的结果,至少在我的环境中是这样.我认为这是有道理的,因为这些值在foo数据表中存储为数字.
是的,v1.8.10中的结果是正确的行为.我们改进了v1.9.2中的舍入方法.这里解释得最好:
在data.table v1.8.10 vs v1.9.2中对非常小的数字(例如1e-28)和0.0进行分组
这意味着我们向后支持整数> 2 ^ 31存储类型numeric.现在已在v1.9.3(可从R-Forge获得)中解决,请参阅新闻:
o
bit64::integer64现在可以分组和加入,#5369.感谢James Sams突出UPC和Clayton Stanley.
提醒:fread()已经能够检测并阅读integer64一段时间了.o
setNumericRounding()当加入或分组类型为numeric#5369的列时,新函数可用于减少1个字节或0个字节的舍入.请参阅?setNumericRoundingv1.9.2中的示例和NEWS项目.getNumericRounding()返回当前设置.
因此,您可以调用setNumericRounding(0)全局关闭所有numeric列的舍入,或者更好,为列使用更合适的类型:bit64::integer64现在支持它.
v1.9.2的变化是:
o数值数据仍然像以前一样在公差范围内连接和分组,但不是公差为sqrt(.Machine $ double.eps)== 1.490116e-08(与base :: all.equal的默认值相同),现在有效数字舍入为最后2个字节,apx 11 sf这更适用于大型(1.23e20)和小型(1.23e-20)数字,并且通过简单的位旋转速度更快.一些函数提供了一个"容差"参数但是没有被传递,所以已被删除.我们的目标是在将来的版本[DONE]中添加一个全局选项(例如,2个,1个或0个字节的舍入).
示例?setNumericRounding是:
> DT = data.table(a=seq(0,1,by=0.2),b=1:2, key="a")
> DT
a b
1: 0.0 1
2: 0.2 2
3: 0.4 1
4: 0.6 2
5: 0.8 1
6: 1.0 2
> setNumericRounding(0) # turn off rounding; i.e. if we didn't round
> DT[.(0.4)] # works
a b
1: 0.4 1
> DT[.(0.6)] # no match!, confusing to users
a b # 0.6 is clearing there in DT, and 0.4 worked ok!
1: 0.6 NA
>
> setNumericRounding(2) # restore default
> DT[.(0.6)] # now works as user expects
a b
1: 0.6 2
>
> # using type 'numeric' for integers > 2^31 (typically ids)
> DT = data.table(id = c(1234567890123, 1234567890124, 1234567890125), val=1:3)
> DT[,.N,by=id] # 1 row (the last digit has been rounded)
id N
1: 1.234568e+12 3
> setNumericRounding(0) # turn off rounding
> DT[,.N,by=id] # 3 rows (the last digit wasn't rounded)
id N
1: 1.234568e+12 1
2: 1.234568e+12 1
3: 1.234568e+12 1
> # but, better to use bit64::integer64 for such ids instead of numeric
> setNumericRounding(2) # restore default, preferred
Run Code Online (Sandbox Code Playgroud)