通过引用函数传递时,处理R data.table中的无效selfref

时间:2014-07-16 23:27:05

标签: r data.table

我的小组使用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,这可能但不理想。有什么想法吗?

2 个答案:

答案 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详细解释了为什么问题中会出现警告。