在函数中通过引用向data.table添加新列并不总是有效

Cla*_*nJY 16 r data.table

在写一个依赖的包时data.table,我发现了一些奇怪的行为.我有一个函数删除和重新排序一些列引用,它工作得很好,这意味着data.table我传入的是修改而不分配函数输出.我有另一个添加列的函数,但这些更改并不总是存在于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
Run Code Online (Sandbox Code Playgroud)

发生了什么?有什么不同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
Run Code Online (Sandbox Code Playgroud)

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

我无法确定所有我看到这种行为的情况,但是保存到RDS并从RDS读取是我能够可靠复制的第一种情况.写入和读取CSV似乎没有同样的问题.

这是一个指针问题这个问题,比如序列化data.table会破坏过度分配的指针吗?有一种简单的方法可以恢复它们吗?我怎么能在我的函数中检查它们,所以如果操作不起作用我可以恢复指针或错误?

我知道我可以将函数输出分配为变通方法,但这不是很好data.table.那还不会在内存中创建一个临时副本吗?

回应Arun的解决方案

Arun已经指示它确实是一个指针问题,可以truelengthsetDT或诊断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
Run Code Online (Sandbox Code Playgroud)

因此看起来函数中的本地副本正在被正确修改,但参考版本不是.为什么不?

Aru*_*run 14

这是一个指针问题这个问题,比如序列化data.table会破坏过度分配的指针吗?

是从磁盘加载将外部指针设置为NULL.我们将不得不再次过度分配.

有一种简单的方法可以恢复它们吗?

是.您可以测试truelength()data.table,如果是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
Run Code Online (Sandbox Code Playgroud)

这可能应该作为常见问题解答(不记得在那里看到它).
已经在警告信息部分的FAQ中.

  • 没理由.我只选择`setDT()`因为`alloc.col`不会无形地返回结果,所以我必须用`invisible()`来包装它.`setDT()`因此似乎更短. (2认同)