将新列添加到函数内的 data.table 引用并不总是有效
Adding new columns to a data.table by-reference within a function not always working
在编写依赖于 data.table
的程序包时,我发现了一些奇怪的行为。我有一个函数可以通过引用删除和重新排序一些列,它工作得很好,这意味着我传入的 data.table
被修改而没有分配函数输出。我有另一个添加 new 列的函数,但是这些更改并不总是保留在传入的 data.table
中。
这是一个小例子:
library(data.table) # I'm using 1.9.4
test <- data.table(id = letters[1:2], val=1:2)
foobar <- function(dt, col) {
dt[, (col) := 1]
invisible(dt)
}
test
# id val
#1: a 1
#2: b 2
saveRDS(test, "test.rds")
test2 <- readRDS("test.rds")
all.equal(test, test2)
#[1] TRUE
foobar(test, "new")
test
# id val new
#1: a 1 1
#2: b 2 1
foobar(test2, "new")
test2
# id val
#1: a 1
#2: b 2
发生了什么事? test2
有什么不同?我可以就地修改现有列:
foobar(test, "val")
test
# id val new
#1: a 1 1
#2: b 1 1
foobar(test2, "val")
test2
# id val
#1: a 1
#2: b 1
但是添加到test2
还是不行:
foobar(test2, "someothercol")
.Last.value
# id val someothercol
#1: a 1 1
#2: b 1 1
test2
# id val
#1: a 1
#2: b 1
我无法确定我看到此行为的所有情况,但保存到 RDS 和从 RDS 读取是我可以可靠复制的第一个情况。写入和读取 CSV 似乎没有相同的问题。
这是一个指针问题吗 this issue,就像序列化一个 data.table 会破坏过度分配的指针一样?有没有简单的方法来恢复它们?我如何在我的函数中检查它们,以便在操作无法正常工作时恢复指针或错误?
我知道我可以将函数输出分配为解决方法,但这不是很 data.table
-y。那不也会在内存中创建一个临时副本吗?
对阿伦解决方案的回应
Arun 说明确实是指针问题,可以用truelength
诊断,用setDT
或alloc.col
修复。我 运行 遇到了一个问题,将他的解决方案封装在一个函数中(从上面的代码继续):
func <- function(dt) {if (!truelength(dt)) setDT(dt)}
func2 <- function(dt) {if (!truelength(dt)) alloc.col(dt)}
test2 <- readRDS("test.rds")
truelength(test2)
#[1] 0
truelength(func(test2))
#[1] 100
truelength(test2)
#[1] 0
truelength(func2(test2))
#[1] 100
truelength(test2)
#[1] 0
所以看起来函数内部的本地副本被正确修改了,但参考版本没有。为什么不呢?
Is this a pointer issue ala this issue, like serializing a data.table destroys the over-allocated pointers?
是,从磁盘加载会将外部指针设置为 NULL。我们将不得不再次过度分配。
Is there a simple way to restore them?
是的。您可以测试 data.table 的 truelength()
,如果是 0
,则在其上使用 setDT()
或 alloc.col()
。
truelength(test2) # [1] 0
if (!truelength(test2))
setDT(test2)
truelength(test2) # [1] 100
foobar(test2, "new")
test2[]
# id val new
# 1: a 1 1
# 2: b 2 1
这应该作为常见问题解答(不记得在那里看到过)。
已在警告消息部分的 FAQ 中。
在编写依赖于 data.table
的程序包时,我发现了一些奇怪的行为。我有一个函数可以通过引用删除和重新排序一些列,它工作得很好,这意味着我传入的 data.table
被修改而没有分配函数输出。我有另一个添加 new 列的函数,但是这些更改并不总是保留在传入的 data.table
中。
这是一个小例子:
library(data.table) # I'm using 1.9.4
test <- data.table(id = letters[1:2], val=1:2)
foobar <- function(dt, col) {
dt[, (col) := 1]
invisible(dt)
}
test
# id val
#1: a 1
#2: b 2
saveRDS(test, "test.rds")
test2 <- readRDS("test.rds")
all.equal(test, test2)
#[1] TRUE
foobar(test, "new")
test
# id val new
#1: a 1 1
#2: b 2 1
foobar(test2, "new")
test2
# id val
#1: a 1
#2: b 2
发生了什么事? test2
有什么不同?我可以就地修改现有列:
foobar(test, "val")
test
# id val new
#1: a 1 1
#2: b 1 1
foobar(test2, "val")
test2
# id val
#1: a 1
#2: b 1
但是添加到test2
还是不行:
foobar(test2, "someothercol")
.Last.value
# id val someothercol
#1: a 1 1
#2: b 1 1
test2
# id val
#1: a 1
#2: b 1
我无法确定我看到此行为的所有情况,但保存到 RDS 和从 RDS 读取是我可以可靠复制的第一个情况。写入和读取 CSV 似乎没有相同的问题。
这是一个指针问题吗 this issue,就像序列化一个 data.table 会破坏过度分配的指针一样?有没有简单的方法来恢复它们?我如何在我的函数中检查它们,以便在操作无法正常工作时恢复指针或错误?
我知道我可以将函数输出分配为解决方法,但这不是很 data.table
-y。那不也会在内存中创建一个临时副本吗?
对阿伦解决方案的回应
Arun 说明确实是指针问题,可以用truelength
诊断,用setDT
或alloc.col
修复。我 运行 遇到了一个问题,将他的解决方案封装在一个函数中(从上面的代码继续):
func <- function(dt) {if (!truelength(dt)) setDT(dt)}
func2 <- function(dt) {if (!truelength(dt)) alloc.col(dt)}
test2 <- readRDS("test.rds")
truelength(test2)
#[1] 0
truelength(func(test2))
#[1] 100
truelength(test2)
#[1] 0
truelength(func2(test2))
#[1] 100
truelength(test2)
#[1] 0
所以看起来函数内部的本地副本被正确修改了,但参考版本没有。为什么不呢?
Is this a pointer issue ala this issue, like serializing a data.table destroys the over-allocated pointers?
是,从磁盘加载会将外部指针设置为 NULL。我们将不得不再次过度分配。
Is there a simple way to restore them?
是的。您可以测试 data.table 的 truelength()
,如果是 0
,则在其上使用 setDT()
或 alloc.col()
。
truelength(test2) # [1] 0
if (!truelength(test2))
setDT(test2)
truelength(test2) # [1] 100
foobar(test2, "new")
test2[]
# id val new
# 1: a 1 1
# 2: b 2 1
这应该作为常见问题解答(不记得在那里看到过)。
已在警告消息部分的 FAQ 中。