【问题标题】:R's data.table Truncating Bits?R的data.table截断位?
【发布时间】:2016-10-04 03:47:52
【问题描述】:

所以我是 R 中的 data.table 的忠实粉丝。我几乎一直使用它,但遇到了一种情况,它根本不适合我。我有一个包(我公司内部的),它使用 R 的 double 来存储一个无符号 64 位整数的值,其位序列对应于一些花哨的编码。这个包在除 data.table 之外的任何地方都能很好地工作。我发现,如果我对这些数据的一列进行聚合,我会丢失大量的唯一值。我在这里唯一的猜测是data.table 在某种奇怪的double 优化中截断位。

任何人都可以确认是这种情况吗?这只是一个错误吗?

下面是问题的重现,并与我目前必须使用但想避免使用的包进行比较 (dplyr)。

temp <- structure(list(obscure_math = c(6.95476896592629e-309, 6.95476863436446e-309, 
6.95476743245288e-309, 6.95476942182375e-309, 6.95477149408563e-309, 
6.95477132830476e-309, 6.95477132830476e-309, 6.95477149408562e-309, 
6.95477174275702e-309, 6.95476880014538e-309, 6.95476896592647e-309, 
6.95476896592647e-309, 6.95476900737172e-309, 6.95476900737172e-309, 
6.95476946326899e-309, 6.95476958760468e-309, 6.95476958760468e-309, 
6.95477020928318e-309, 6.95477124541406e-309, 6.95476859291965e-309, 
6.95476875870014e-309, 6.95476904881676e-309, 6.95476904881676e-309, 
6.95476904881676e-309, 6.95476909026199e-309, 6.95476909026199e-309, 
6.95476909026199e-309, 6.95476909026199e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.9547691317072e-309, 
6.9547691317072e-309, 6.9547691317072e-309, 6.95477211576406e-309, 
6.95476880014538e-309, 6.95476880014538e-309, 6.95476880014538e-309, 
6.95476892448104e-309, 6.95476880014538e-309, 6.95476892448105e-309, 
6.9547689659263e-309, 6.95476913170719e-309, 6.95476933893334e-309
)), .Names = "obscure_math", class = c("data.table", "data.frame"), row.names = c(NA, 
-50L))

dt_collapsed <- temp[, .(count=.N), by=obscure_math]
nrow(dt_collapsed) == length(unique(temp$obscure_math))

setDF(temp)
dplyr_collapsed <- temp %>% group_by(obscure_math) %>% summarise(count=n())
nrow(dplyr_collapsed) == length(unique(temp$obscure_math))

【问题讨论】:

  • 我可以在 data.table 1.9.6 上复制它。 aggregate 也给出 26 行,但 data.table 给出 21。使用 by=as.character(obscure_math) 也给出正确的 26 行
  • 我也可以使用 v1.9.7 进行复制
  • @thelatemail 我不确定这是否重要。包不应该在这样的操作中改变用户数据的值。 by 变量的值应该被改变是非常不透明的。我的猜测是这与优化基数排序有关,但没有人应该期望这种操作会损失精度(在这种情况下完全不可用)。
  • 我们建议使用 bit64::integer64 处理 64 位整数(而不是双精度数)。请参阅?setNumericRounding(和示例)了解我们为什么要四舍五入最后 2 个字节。有一个与此相关的问题(但 w.r.t. 订购 data.table,而不是分组),但我们还没有时间了解如何最好地处理它,#1642
  • @Arun 哇。这让我大吃一惊,默认情况下,您在这些操作中舍入所有双精度数的最后 16 位。据我所知,这是一个糟糕的设计决定。您正在对“大量数字”的构成做出非常大的假设。令人失望。请添加为答案,以便我接受并关闭此问题。

标签: r data.table


【解决方案1】:

更新:current development version of data.table (v1.9.7) 中的默认舍入功能已被删除。请参阅开发版here 的安装说明。

这也意味着您有责任了解表示浮点数并处理它的局限性。


data.table 已经存在很长时间了。我们过去使用 threshold 来处理浮点表示的限制(就像 base R 所做的那样,例如,all.equal)。然而,它根本不起作用,因为它需要根据比较的数字有多大进行自适应。 This series of articles 是关于这个主题和其他潜在问题的优秀读物。

这是一个反复出现的问题,因为 a) 人们没有意识到限制,或者 b) 阈值并没有真正解决他们的问题,这意味着人们一直在这里提问或在项目页面上发帖.

虽然我们将 data.table 的顺序重新实现为快速基数排序,但我们借此机会提供了另一种解决问题的方法,并在证明不受欢迎时提供了一种解决方法(导出 setNumericRounding)。对于 #1642 问题,ordering 可能不需要对双精度数进行舍入(但这并不是那么简单,因为顺序直接影响基于二进制搜索的子集)。

这里的实际问题是对浮点数进行分组,更糟糕的是像你这样的数字。恕我直言,这只是一个糟糕的选择。

我可以想到两种前进的方式:

  1. 在对真正双精度的列进行分组时(在 R 中,1 是双精度而不是 1L,并且这些情况没有问题),我们会提供一个警告,即最后 2 个字节被四舍五入,并且人们应该阅读?setNumericRounding。并且还建议使用bit64::integer64

  2. 删除允许对真正双精度值进行分组 操作的功能,或强制它们在继续之前将精度固定为某些数字。我想不出一个真正想要按浮点数分组的正当理由(很想听听这样做的人的意见)。

不太可能发生的是返回到基于阈值的检查,以识别哪些双打应该属于同一组。

为了让 Q 得到回答,请使用 setNumericRounding(0L)

【讨论】:

  • 对双打进行分组时出现的明显错误点赞。
  • @DirkEddelbuettel,谢谢。我已经提交了#1728。我同意这是最好的前进方式。
  • 干杯,谢谢!我会建议警告并建议按双打分组。我能想到一个用户会分组为双的原因。这是我的问题:)
  • @iShouldUseAName,抱歉,这不是正当理由。为此,您公司的软件包应开始使用 64 位整数(使用 bit64 或您自己的内部实现)。不要滥用/玩双打。
  • @Arun 它也出现在data.table 源代码中,我可以将integer64 作为一个类添加到我的列中,并在仍然使用data.table 的同时解决这个问题。再次感谢 data.table 和我的问题所做的所有出色工作!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-08
  • 1970-01-01
  • 2022-01-11
  • 1970-01-01
  • 2013-10-08
  • 1970-01-01
相关资源
最近更新 更多