R 在函数中通过引用向data.table添加新列并不总是有效
在编写一个依赖于R 在函数中通过引用向data.table添加新列并不总是有效,r,data.table,R,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 <
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
但是添加到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写入和从CSV读取似乎没有相同的问题
这是指针问题吗,就像序列化data.table会破坏过度分配的指针一样?有没有简单的方法来恢复它们?如何在函数中检查它们,以便在操作不起作用时恢复指针或错误
我知道我可以将函数输出指定为一种解决方法,但这不是很好的数据。这不也会在内存中创建一个临时副本吗
对阿伦解决方案的回应
Arun指出,这确实是一个指针问题,可以通过truelength
诊断,并通过setDT
或alloc.col
修复。我在将他的解决方案封装到函数中时遇到了一个问题(从上面的代码继续):
func
这是指针问题还是这个问题,就像序列化data.table会破坏过度分配的指针一样
是从磁盘加载将外部指针设置为NULL。我们将不得不再次超额分配
有没有简单的方法来恢复它们
对。您可以测试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
这可能会成为常见问题解答(不记得在那里看到过)。
已在警告消息部分中
这是指针问题还是这个问题,就像序列化data.table会破坏过度分配的指针一样
是从磁盘加载将外部指针设置为NULL。我们将不得不再次超额分配
有没有简单的方法来恢复它们
对。您可以测试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
这可能会成为常见问题解答(不记得在那里看到过)。
已经在警告信息部分。精彩的问题/解释/重复的例子/动机。荣誉我甚至可以在开发版本上复制这一点。顺便说一句,在你的函数IMO中,你不需要不可见(dt)
部分。@DavidArenburg谢谢!我知道我本可以省略它,但出于某种原因,我想禁止打印。再想一想,显示函数输出和原始data.table之间的差异可能会更清楚。哦,好吧。难以置信的问题/解释/可复制的例子/动机。荣誉我甚至可以在开发版本上复制这一点。顺便说一句,在你的函数IMO中,你不需要不可见(dt)
部分。@DavidArenburg谢谢!我知道我本可以省略它,但出于某种原因,我想禁止打印。再想一想,显示函数输出和原始data.table之间的差异可能会更清楚。哦,好吧。有没有理由选择setDT
而不是alloc.col
?没有理由。我只是选择了setDT()
,因为alloc.col
不会以不可见的方式返回结果,所以我必须用invisible()
来包装它<代码>setDT()
因此看起来更短。哦,好吧,这是有道理的。因为它将在函数中,所以这一步的不可见性并不重要,所以我认为alloc.col
更好地传达意图。感谢您快速准确的回复@Arun!实际上,这在函数内部似乎不起作用,无论是setDT
还是alloc.col
。我可以使用browser
查看块触发器和bump uptruelength
,但是我传入的表仍然没有修改,调用后它的truelength
仍然是0,例如func!这让我得出了一个我希望避免的结论(必须重新分配到某个地方),但至少副本是肤浅的。是否有理由选择setDT
而不是alloc.col
?没有理由。我只是选择了setDT()
,因为alloc.col
不会以不可见的方式返回结果,所以我必须用invisible()
来包装它<代码>setDT()
因此看起来更短。哦,好吧,这是有道理的。因为它将在函数中,所以这一步的不可见性并不重要,所以我认为alloc.col
更好地传达意图。感谢您快速准确的回复@Arun!实际上,这在函数内部似乎不起作用,无论是setDT
还是alloc.col
。我可以使用browser
查看块触发器和bump uptruelength
,但是我传入的表仍然没有修改,调用后它的truelength
仍然是0,例如func!这让我得出了一个我希望避免的结论(必须重新分配到某个地方),但至少副本是肤浅的。
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