舍入到倍数并过滤data.table

kis*_*msu 4 r data.table

我有一个非常有趣的问题,但我宁愿没有.我必须将一个数字舍入到一个结束的数字,所以我按照这里的解决方案 它曾经工作正常,直到我发现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
Run Code Online (Sandbox Code Playgroud)

公平地说,data.frame即使第一个过滤器也不起作用.任何想法如何解决?

Dav*_*urg 5

只是为@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")
Run Code Online (Sandbox Code Playgroud)

所以我们来解决上面的几点.因为你有一个浮点和引用?setNumericRounding

计算机不能使用基数2精确地表示某些浮点数(例如0.6).这会在连接或分组"数字"类型的列时导致意外行为; 即'双倍

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

在v1.9.8之前,setNumericRounding(2)是默认值(因此您的第一个示例有效),但在用户对GH(IIRC)不一致的一些投诉之后,自v1.9.8以来,默认设置setNumericRounding(0)为了与data.frame行为一致(参见在这里).因此,如果您将data.table更新为最新版本,您将看到两个示例的相同data.tabledata.frame行为相同(并且两个示例都将失败).

相比

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

setNumericRounding(1)
DT[a == 112.3]
#                     a b
# 1: 112.30000000000001 B
Run Code Online (Sandbox Code Playgroud)

所以你会问," 地球上的基数算法与这里的任何东西有什么关系 ".所以我们在这里达到第三点 - 二级指数(请阅读此内容).让我们看看当你运行上面的代码时实际发生了什么

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
Run Code Online (Sandbox Code Playgroud)

让我们检查新的二级指数

indices(DT)
#[1] "a"
Run Code Online (Sandbox Code Playgroud)

当你运行时==,data.table设置a为你的二级索引,以便更有效地执行未来的操作(这在v1.9.4中介绍,请参见此处).换句话说,你执行二进制连接a而不是像之前的v1.9.4一样的常规矢量扫描(作为旁注,这可以通过执行来禁用options(datatable.auto.index = FALSE),在这种情况下,setNumericRounding(1)除非你愿意,否则你的例子都不会起作用使用setkeyon参数显式指定键)

这可能也解释了原因

DT[a == 112.30000 & b == 'B'] 
Run Code Online (Sandbox Code Playgroud)

不起作用.你在这里通过两列进行子设置,并且二级索引或二进制连接都没有(自动)启动表达式(例如== & ==),因此你进行了正常的矢量扫描而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
Run Code Online (Sandbox Code Playgroud)

或者使用旧的方式

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