【问题标题】:Round to a multiple and filter in data.table在 data.table 中四舍五入并过滤
【发布时间】:2016-12-19 13:38:07
【问题描述】:

我有一个非常有趣的问题,虽然我不想有一个。 我必须将一个数字四舍五入,所以我遵循了解决方案here 它曾经工作正常,直到我发现 data.table 的错误

library(data.table)
options(digits = 20) # to see number representation
mround <- function (number, multiple) {
   return(multiple * round(number / multiple))
}
DT = data.table(a = mround(112.3, 0.1), b = "B")
DT[a == 112.3,] # works as expected, i.e returns one row
DT[a == 112.3 & b == 'B', ] # doesn't work

公平地说,data.frame 甚至第一个过滤器也不起作用。任何想法如何解决这个问题?

【问题讨论】:

  • 你的问题没有明确定义。您应该添加您期望的输出。这两行都为我返回了空的 data.tables。
  • data.table 中的第一个语句对我不起作用,与data.tabledata.frame 无关,但这是floating point precision 的问题。请参阅DT[(a - 112.3)&lt;1.e-6 &amp; b == 'B',](使用 0.000001 的误差范围,如果您想要更精确,请参阅上面的链接或使用 .Machine$double.eps^0.5all.equal 一样)
  • FWIW,round 需要一个digits 参数,round(112.3,1) 应该足够了,应该避免你的实际问题)
  • @lmo 我希望在此过滤器中看到一行
  • @Tensibai round(112.3,1) 是一种解决方案,但不是通用的,因为我需要将函数舍入到 0.5

标签: r data.table


【解决方案1】:

只是为了添加到@Tens 很棒的答案。

似乎发生了三件事

  • 您遇到了浮点问题(如前所述)
  • 您使用的是旧data.table版本
  • 二级索引在您不知情的情况下开始发挥作用

使用您的设置

library(data.table)
options(digits = 20) # to see number representation

mround <- function (number, multiple) {
  return(multiple * round(number / multiple))
}

DT = data.table(a = mround(112.3, 0.1), b = "B")

所以让我们解决上面的问题。因为你有一个浮点并引用?setNumericRounding

计算机无法使用以 2 为底的精确表示某些浮点数(例如 0.6)。这会导致在连接或分组“数字”类型的列时出现意外行为;即'双

这导致 data.table 开发人员实现 setNumericRounding 自动舍入浮点数,因此基数算法将按预期运行。

在 v1.9.8 之前,setNumericRounding(2) 是默认值(因此您的第一个示例有效),但在用户抱怨 GH (IIRC) 不一致之后,自 v1.9.8 起,默认值设置回 setNumericRounding(0)为了与data.frame 行为一致(请参阅here)。因此,如果您将 data.table 更新到最新版本,您将看到 data.tabledata.frame 对于两个示例的行为相同(并且您的两个示例都将失败)。

比较

setNumericRounding(0)
DT[a == 112.3]
## Empty data.table (0 rows) of 2 cols: a,b

setNumericRounding(1)
DT[a == 112.3]
#                     a b
# 1: 112.30000000000001 B

所以你会问,“到底基数算法与这里的任何东西有什么关系”。所以在这里我们到达了上面的第三点 - 二级索引(请阅读this)。让我们看看当你运行上面的代码时实际发生了什么

options(datatable.verbose = TRUE)
DT[a == 112.3] # works as expected, i.e returns one row
# Creating new index 'a' <~~~~
# forder took 0 sec
# Starting bmerge ...done in 0 secs
#                     a b
# 1: 112.30000000000001 B

让我们检查新的二级索引

indices(DT)
#[1] "a"

当您运行 == 时,data.table 将 a 设置为您的二级索引,以便更有效地执行未来的操作(这是在 v1.9.4 中引入的,请参阅 here)。换句话说,您在a 上执行了二进制连接,而不是像以前的 v1.9.4 那样进行通常的矢量扫描(作为旁注,这可以通过执行 options(datatable.auto.index = FALSE) 来禁用,在这种情况下,您的任何示例都不会即使使用 setNumericRounding(1) 也可以使用,除非您使用 setkeyon 参数明确指定密钥)

这大概也能解释为什么

DT[a == 112.30000 & b == 'B'] 

不起作用。您在此处通过两列进行子设置,并且二级索引或二元连接都不会(自动)启动诸如== &amp; ==(尚未)之类的表达式,因此您进行了法线向量扫描,而setNumericRounding(1) 没有开始吧

不过,您可以手动设置键并使其工作,例如(就像我在@Tens 答案下评论的那样),您可以这样做

setNumericRounding(1) # make sure autoroundings turned on
DT[.(112.3, 'B'), nomatch = 0L, on = .(a, b)]
# Calculated ad hoc index in 0 secs
# Starting bmerge ...done in 0 secs
#        a b
# 1: 112.3 B

或者使用旧方法

setkey(DT, a, b)
DT[.(112.3, 'B'), nomatch = 0L]
# Starting bmerge ...done in 0 secs
#        a b
# 1: 112.3 B

【讨论】:

  • 感谢您的总结。我实际上并没有关于临时索引,忘记了这个功能。
【解决方案2】:

这是floating point precision 的问题。 请参阅DT[abs(a - 112.3)&lt;1.e-6 &amp; b == 'B',],使用 0.000001 的误差范围会给您正确的结果。

如果您想要更精确,可以使用.Machine$double.eps^0.5all.equal

一般建议是永远不要比较浮点数的相等性,而是将差异与足够接近机器精度的值进行比较,以绕过 0 和 1 之间的精度漂移),更多细节here

解决问题的一种方法是将函数重构为:

mround <- function (number, multiple, digits=nchar(strsplit(as.character(multiple),".",fixed=TRUE)[[1]][2])) {

   round(multiple * round(number / multiple),digits)
}

我使用“卷积”方法从作为默认有效数字传递的多个中获取所需的数字,以适应您的需要(例如,您可以在这里使用 2,或者在调用时强制精度)。
我删除了不必要的return,这只会导致解释器在函数调用结束时查找已经调用的函数。

这样你的输出应该足够精确,但你仍然会遇到极端情况:

> mround(112.37,0.2)
[1] 112.40000000000001

要在连接中使用浮点数,您可以使用(由 David Arenburg 提供):

setNumericRounding(1)
DT[.(112.3, 'B'), nomatch = 0L, on = .(a, b)]

【讨论】:

  • 我同意,但我想用这个专栏加入。无法想象没有人使用浮动来加入。
  • @kismsu 我不明白你的意思...这是怎么回事? (听起来是另一个问题)
  • @kismsu 我的意思是DT1[DT2,on=.((x.a-i.a &lt; .Machine$double.eps^0.5)] 应该这样做(未经测试)
  • 我以为你找不到相等的行,那么你将无法在 inner_join (merge) 中看到它
  • 您还不能将表达式传递给 on 参数 IIRC。在某个地方应该有一个 PR,如果没有,肯定应该创建它。
猜你喜欢
  • 2017-03-23
  • 2013-07-31
  • 2012-08-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-12
相关资源
最近更新 更多