我的小组使用data.table写了很多代码,我们偶尔会被'检测到无效的.internal.selfref并通过获取整个表的副本来修复...'警告。当通过引用函数传递数据表时,此行为可能会破坏我们的代码,并且我试图弄清楚如何解决它。
假设我有一个函数,它将一个列添加到data.table作为副作用 - 注意不返回原始的data.table。
foo <- function(mydt){
mydt[, c := c("a", "b")]
return(123)
)
> x<- data.table(a=c(1,2), b=c(3,4))
> foo(x)
[1] 123
> x
a b c
1: 1 3 a
2: 2 4 b
x已使用新列更新。这是理想的行为。
现在假设发生了一些违反x:
内部自我引用的事情> x<- data.table(a=c(1,2), b=c(3,4))
> x[["a"]] <- c(7,8)
> foo(x)
[1] 123
Warning message:
In `[.data.table`(mydt, , `:=`(c, c("a", "b"))) :
Invalid .internal.selfref detected and fixed by taking a copy ...
> x
a b
1: 7 3
2: 8 4
我明白发生了什么(大多数情况下)。 [[“a”]]结构不是data.table友好; x被转换为数据框,然后返回到数据表,这在某种程度上搞砸了内部工作。然后在foo()内部,在添加列的引用操作期间,检测到该问题,并且制作了mydt的副本;新列“c”已添加到mydt。但是,该复制操作切断了x和mydt之间的传递引用关系,因此附加列不是x的一部分。
函数foo()将被不同的人使用,并且很难防止无效的内部selfref情况。有人可能很容易做一些像x [[“a”]]这样会导致无效输入的东西。我正在试图弄清楚如何从foo里面处理这个问题。
到目前为止,我有这个想法,在foo()的开头:
if(!data.table:::selfrefok(mydt)) stop("mydt is corrupt.")
这至少让我们有机会发现问题,但它对foo()的用户不是很友好,因为这些输入可能被破坏的方式可能非常不透明。理想情况下,我希望能够纠正损坏的输入并维护foo()的所需功能。但我无法看到如何,除非我重构我的代码,以便foo返回mydt并将其分配给调用范围中的x,这可能但不理想。有什么想法吗?
答案 0 :(得分:3)
你应该阅读整个警告......
然后你会注意到
在较早的时候,此data.table已由R复制(或使用structure()或类似方法手动创建)。避免使用键&lt; - ,名称&lt; - 和attr&lt; - 当前(并且奇怪地)在R中可以复制整个data.table。
[[<-
与names<-
和attr<-
类似,因为它会创建副本。
您可以确保by-reference行为是使用substitute构造调用,然后在父框架中进行评估
foo <- function(x) {
l <- substitute(x[,c := 'a'], as.list(match.call())['x']);
eval.parent(l)
return(123)}
xx<- data.table(a=c(1,2), b=c(3,4))
xx[["a"]] <- c(7,8)
foo(xx)
# [1] 123
# Warning message: .....
# but it now works!
xx
# a b c
# 1: 7 3 a
# 2: 8 4 a
警告仍然存在,但功能可以正常工作。
答案 1 :(得分:2)
@pteehan,好问题!在我看来,一个更清洁的修复方法是在分配步骤中恢复过度分配,使用警告基本上说&#34;不要这样做!&#34;
这样做的方法是通过当前不存在的[[<-.data.table
方法。除非我遗漏了某些内容,否则它将是一个很好的补充,其目的是不鼓励使用它,但是要抓住这样的案例并引导人们正确使用(使用警告),同时恢复过度分配。
大致是:
`[[<-.data.table` <- function(x, i, j, value) {
warning("Don't do this. Use := instead.")
call = sys.call()
call[[1L]] = `[[<-.data.frame`
ans = copy(eval(call, envir=parent.frame()))
}
foo <- function(mydt) {
mydt[, c := c("a", "b")]
return(123)
}
x <- data.table(a = c(1,2), b = c(3,4))
x[["a"]] <- c(7,8)
# Warning message:
# In `[[<-.data.table`(`*tmp*`, "a", value = c(7, 8)) :
# Don't do this. Use := instead.
data.table:::selfrefok(x)
# [1] 1
foo(x)
# [1] 123
x
# a b c
# 1: 7 3 a
# 2: 8 4 b
我认为,这些方面应该提供更清晰的解决方案。也许这应该得到实施。
PS:This post详细解释了为什么问题中会出现警告。