这个很奇怪。不确定我是否遗漏了某些内容,或者它是data.table
中的错误还是fread
中的错误。
我正在尝试"拉伸"一个缺少一个时间点的时间序列的数据表。从文件中读取此表时,X[Y] 联接将填充缺失行中的 NA,也会填充存在数据点的其他行中的 NA。仅当用于键控的t
列包含浮点数而不是整数时,才会发生这种情况。
library(data.table)
# This works fine; empty row at t=0.5
# is filled with NA after join
dt = data.table(id = as.integer(rep(0, 10)),
t = seq(0.1, 1, 0.1),
y = 1:10,
key = "id,t")
dt = dt[!(t == 0.5)]
dtAux = dt[,
.(seq(min(t), max(t), 0.1)),
by = id]
setkey(dtAux, id, V1)
dt[dtAux]
id t y
1: 0 0.1 1
2: 0 0.2 2
3: 0 0.3 3
4: 0 0.4 4
5: 0 0.5 NA
6: 0 0.6 6
7: 0 0.7 7
8: 0 0.8 8
9: 0 0.9 9
10: 0 1.0 10
# This fails; NA’s created in multiple rows
fwrite(dt, "test.csv", row.names = F)
dtFromFile = fread("test.csv")
setkey(dtFromFile, id, t)
dtAux = dtFromFile[,
.(seq(min(t), max(t), 0.1)),
by = id]
setkey(dtAux, id, V1)
dtFromFile[dtAux]
id t y
1: 0 0.1 1
2: 0 0.2 2
3: 0 0.3 NA
4: 0 0.4 4
5: 0 0.5 NA
6: 0 0.6 6
7: 0 0.7 NA
8: 0 0.8 8
9: 0 0.9 9
10: 0 1.0 10
在 3.6.1 上使用data.table
1.12.4
> sessionInfo()
R version 3.6.1 (2019-07-05)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux bullseye/sid
Matrix products: default
BLAS: /usr/lib/x86_64-linux-gnu/blas/libblas.so.3.8.0
LAPACK: /usr/lib/x86_64-linux-gnu/lapack/liblapack.so.3.8.0
locale:
[1] LC_CTYPE=en_US.UTF-8 LC_NUMERIC=C
[3] LC_TIME=en_US.UTF-8 LC_COLLATE=en_US.UTF-8
[5] LC_MONETARY=en_US.UTF-8 LC_MESSAGES=en_US.UTF-8
[7] LC_PAPER=en_US.UTF-8 LC_NAME=C
[9] LC_ADDRESS=C LC_TELEPHONE=C
[11] LC_MEASUREMENT=en_US.UTF-8 LC_IDENTIFICATION=C
attached base packages:
[1] stats graphics grDevices utils datasets methods base
other attached packages:
[1] data.table_1.12.4
loaded via a namespace (and not attached):
[1] compiler_3.6.1 tools_3.6.1
来自?setNumericRounding
计算机不能使用基数 2 精确表示某些浮点数(例如 0.6(。这 在联接或分组类型为"数字"的列时导致意外行为;即"双倍", 请参阅下面的示例。在不希望的情况下,data.table 允许将此类数据向上舍入为 大约 11 平方英尺,在许多情况下是很多数字。这是通过将 有效数的最后 2 个字节。其他可能的值为 1 字节舍入或不舍入(完整 精度,默认值(。 它是字节而不是位,因为它与用于对数字进行排序的基数排序算法相关联 逐个字节排序。默认舍入为 0 字节时,最多需要 8 次传递。跟 舍入 2 个字节,最多需要 6 次传递(因此可能更快一点(。 对于大数字(整数> 2^31(,我们建议使用 bit64::integer64,即使 默认值为舍入 0 字节(全精度(。
要修复它,您可以在运行代码之前使用以下方法。
setNumericRounding(2)
有关更多示例,请参阅?setNumericRounding
,以及舍入到多个并在 data.table 中过滤
编辑:OP询问为什么它在脚本中工作,而不是从文件中读取数据时。这可能是由于ALTREP(请参阅此处的幻灯片9(。
library(data.table) #data.table_1.12.2 R Win x64
dt_seq <- data.table(x = seq(0.1, 0.5, 0.2), v=1:3, key = "x")
dt_c <- data.table(x = c(0.1,0.3,0.5), v=1:3, key = "x")
dtAux_seq = data.table(x=seq(0.1, 0.5, 0.1), key="x")
dtAux_c = data.table(x=c(0.1,0.2,0.3,0.4,0.5), key="x")
fwrite(dt_seq, "test.csv")
dtFromFile <- fread("test.csv", key="x")
测试差异联接:
> dt_seq[dtAux_seq]
x v
1: 0.1 1
2: 0.2 NA
3: 0.3 2
4: 0.4 NA
5: 0.5 3
> dt_c[dtAux_seq]
x v
1: 0.1 1
2: 0.2 NA
3: 0.3 NA
4: 0.4 NA
5: 0.5 3
> dtFromFile[dtAux_seq]
x v
1: 0.1 1
2: 0.2 NA
3: 0.3 NA
4: 0.4 NA
5: 0.5 3
在dtAux_c
中显式键入值:
> dt_seq[dtAux_c]
x v
1: 0.1 1
2: 0.2 NA
3: 0.3 NA
4: 0.4 NA
5: 0.5 3
> dt_c[dtAux_c]
x v
1: 0.1 1
2: 0.2 NA
3: 0.3 2
4: 0.4 NA
5: 0.5 3
> dtFromFile[dtAux_c]
x v
1: 0.1 1
2: 0.2 NA
3: 0.3 2
4: 0.4 NA
5: 0.5 3
这是由于十进制值上的浮点错误。这不是特定于 R 的问题,而只是计算机处理小数的方式。
阅读更多信息:为什么这些数字不相等?
在连接十进制值时,解决方案是先将它们四舍五入到相关的位数。
dtAux2 = dtFromFile[,
.( round( seq(min(t), max(t), 0.1), digits = 1 ) ),
by = id]
dt_Aux2
setkey(dtAux2, id, V1)
dtFromFile[dtAux2]
# id t y
#1: 0 0.1 1
#2: 0 0.2 2
#3: 0 0.3 3
#4: 0 0.4 4
#5: 0 0.5 NA
#6: 0 0.6 6
#7: 0 0.7 7
#8: 0 0.8 8
#9: 0 0.9 9
#10: 0 1.0 10