r - 在 R 3.4.3 和 R 3.6.0 之间更改存储在 data.table 中的闭包行为
问题描述
当我从 R 3.4.3 升级到 R 3.6.0(两者都使用 data.table 1.12.6)时,我注意到了以下特殊行为。在 3.4.3 中,下面的代码导致 all.equal 语句为 TRUE,但在 3.6.0 中存在一个平均相对差异,这是因为即使我们试图访问从组“a”计算的 approxfun,使用“b”组中的值(可能由于惰性评估)。在 3.6.0 中,这个问题可以通过在调用 approxfun 时添加一个复制语句来解决,基于这个问题: Handling ofclosures in data.table
令我着迷的是,我在 3.4.3 中没有出错。知道发生了什么变化吗?
library(data.table)
data <- data.table(
group = c(rep("a", 4), rep("b", 4)),
x = rep(c(.02, .04, .12, .21), 2),
y = c(
0.0122, 0.01231, 0.01325, 0.01374, 0.01218, 0.01229, 0.0133, 0.01379)
)
dtFuncs <- data[ , list(
func = list(stats::approxfun(x, y, rule = 2))
), by = group]
f <- function(group, x) {
dtResults <- CJ(group = group, x = x)
dtResults <- dtResults[ , {
.g <- group
f2 <- dtFuncs[group == .g, func][[1]]
list(x = x, y = f2(x))
}, by = group]
dtResults
}
x0 <- .07
g <- "a"
all.equal(
with(data[group == g], approx(x, y, x0, rule = 2)$y),
f(group = g, x = x0)$y
)
解决方案
在 r-source 上运行 git bisect 后,我能够推断出是这个提交导致了这种行为:https ://github.com/wch/r-source/commit/adcf18b773149fa20f289f2c8f2e45e6f7b0dbfe
从根本上发生的情况是,在 x 以 approxfun 排序的情况下,不再制作内部副本。如果数据被随机排序,代码将继续工作!(见下面的片段)
对我来说,最好不要将复杂的对象与 data.table 混合使用,因为每个“by”组都一遍又一遍地使用相同的环境(或者对 data.table::copy 非常谨慎)
## should be run under R > 3.6.0 to see disparity
library(data.table)
## original sorted x (does not work)
data <- data.table(
group = c(rep("a", 4), rep("b", 4)),
x = rep(c(.02, .04, .12, .21), 2),
y = c(
0.0122, 0.01231, 0.01325, 0.01374, 0.01218, 0.01229, 0.0133, 0.01379)
)
dtFuncs <- data[ , {
print(environment())
list(
func = list(stats::approxfun(x, y, rule = 2))
)
}, by = group]
f <- function(group, x) {
dtResults <- CJ(group = group, x = x)
dtResults <- dtResults[ , {
.g <- group
f2 <- dtFuncs[group == .g, func][[1]]
list(x = x, y = f2(x))
}, by = group]
dtResults
}
get("y", environment(dtFuncs$func[[1]]))
get("y", environment(dtFuncs$func[[2]]))
x0 <- .07
g <- "a"
all.equal(
with(data[group == g], approx(x, y, x0, rule = 2)$y),
f(group = g, x = x0)$y
)
## unsorted x (works)
data <- data.table(
group = c(rep("a", 4), rep("b", 4)),
x = rep(c(.02, .04, .12, .21), 2),
y = c(
0.0122, 0.01231, 0.01325, 0.01374, 0.01218, 0.01229, 0.0133, 0.01379)
)
set.seed(10)
data <- data[sample(1:.N, .N)]
dtFuncs <- data[ , {
print(environment())
list(
func = list(stats::approxfun(x, y, rule = 2))
)
}, by = group]
f <- function(group, x) {
dtResults <- CJ(group = group, x = x)
dtResults <- dtResults[ , {
.g <- group
f2 <- dtFuncs[group == .g, func][[1]]
list(x = x, y = f2(x))
}, by = group]
dtResults
}
get("y", environment(dtFuncs$func[[1]]))
get("y", environment(dtFuncs$func[[2]]))
x0 <- .07
g <- "a"
all.equal(
with(data[group == g], approx(x, y, x0, rule = 2)$y),
f(group = g, x = x0)$y
)
## better approach: maybe safer to avoid mixing objects treated by reference
## (data.table & closures) all together...
fList <- lapply(split(data, by = "group"), function(x){
with(x, stats::approxfun(x, y, rule = 2))
})
fList
fList[[1]](.07) != fList[[2]](.07)
推荐阅读
- c# - 从 WebService 传递对象
- qt - Qt qml MediaPlayer/Video 仅再现声音(无视频)
- html - 使用 CSS 维护 ID attr 更改
- java - 使用 (:date INTERVAL :field HOUR) 创建 JPQL 语句
- jhipster - 从具有密码授予类型的移动应用程序请求的正确方法是什么?
- php - PHP在不满足条件的情况下输入if-condition
- javascript - 使用 D3.js 的 3D 条形图的透视问题
- wix - 我们可以将 KeyPath="no" 与“组件”元素与注册表一起使用吗
- .net - Windows 工作流:如何在发送活动中为 OperationName 参数指定命名空间 URI?
- c++ - iMAC 上的 Qt5.11.2、QSqlDatabase 和 MariaDB