函数内使用:=赋值的data.table对象未被打印出来。

48

我想在一个函数内修改一个 data.table。如果我在函数内使用 := 特性,结果仅会在第二次调用时打印。

看下面的例子:

library(data.table)
mydt <- data.table(x = 1:3, y = 5:7)

myfunction <- function(dt) {
    dt[, z := y - x]
    dt
}

当我仅调用函数时,表格不会被打印(这是标准行为)。然而,如果我将返回的 data.table 保存到一个新对象中,在第一次调用时它不会被打印出来,只有在第二次调用时才会。

myfunction(mydt)  # nothing is printed   
result <- myfunction(mydt) 
result  # nothing is printed
result  # for the second time, the result is printed
mydt                                                                     
#    x y z
# 1: 1 5 4
# 2: 2 6 4
# 3: 3 7 4 

你能解释一下为什么会发生这种情况以及如何预防吗?


6
请阅读此链接:https://github.com/Rdatatable/data.table/blob/master/NEWS.md#bug-fixes-3。v1.9.6中修复的第一个BUG。 - David Arenburg
请参阅 data.table 常见问题解答的第 2.23 节 (vignette("datatable-faq", package = "data.table"))。 - Uwe
1
更新@DavidArenburg的链接,现在应该是https://github.com/Rdatatable/data.table/blob/master/NEWS.0.md#bug-fixes-1(因为“NEWS.md”被截断,许多内容转移到了“NEWS.0.md”)。 - r2evans
2个回答

55

在版本1.9.6中修复了一个错误,但同时引入了这个问题(请参见NEWS 1.9.6,BUG FIXES#1)。

应该在函数末尾调用DT []以防止出现此行为。

myfunction <- function(dt) {
    dt[, z := y - x][]
}
myfunction(mydt)  # prints immediately
#    x y z
# 1: 1 5 4
# 2: 2 6 4
# 3: 3 7 4 

这在data.table FAQ 2.23中有详细描述:

为什么在使用:=将结果打印到控制台后,有时需要输入两次DT

这是使用#869的一个不幸的缺陷。如果在函数内部使用:=,并且在函数结束之前没有DT[],那么下一次在提示符处键入DT时将不会打印任何内容。重复键入DT将会打印。为了避免这种情况:请在函数中最后一个:=之后包含一个DT[]。如果这不可能(例如,它不是您可以更改的函数),那么在提示符处键入print(DT)DT[]将保证打印。与以前一样,在:=查询的末尾添加一个额外的[]是一个推荐的习惯用语,以更新并打印;例如> DT[,foo:=3L][]

9
只有在禁止数据表打印时才需要使用DT[],因此在使用: =set*函数时需要。 - jangorecki

-3

如果我在这里发布的不是答案,我很抱歉,但我的帖子太长了,无法作为评论。

我想指出,janosdivenyi的解决方案,在dt后添加一个尾随的[]并不能总是产生预期的结果(即使使用data.table 1.9.6或1.10.4),如下所示。

以下示例显示,如果dt是函数中的最后一行,则可以在没有尾随[]的情况下获得所需的行为,但是如果dt不在函数的最后一行,则需要一个尾随[]才能获得所需的行为。

第一个示例显示,当dt位于函数的最后一行时,没有尾随[]的情况下,我们会得到预期的行为。

mydt <- data.table(x = 1:3, y = 5:7)

myfunction <- function(dt) {
  df <- 1
  dt[, z := y - x]
}

myfunction(mydt)  # Nothing printed as expected

mydt  # Content printed as desired
##    x y z
## 1: 1 5 4
## 2: 2 6 4
## 3: 3 7 4

dt后面添加一个尾随的[]会导致意外的行为

mydt <- data.table(x = 1:3, y = 5:7)

myfunction <- function(dt) {
  df <- 1
  dt[, z := y - x][]
}

myfunction(mydt)  # Content printed unexpectedly
##    x y z
## 1: 1 5 4
## 2: 2 6 4
## 3: 3 7 4

mydt  # Content printed as desired
##    x y z
## 1: 1 5 4
## 2: 2 6 4
## 3: 3 7 4

df <- 1 移动到没有尾随 [] 的 dt 后会产生意外行为。
mydt <- data.table(x = 1:3, y = 5:7)

myfunction <- function(dt) {
  dt[, z := y - x]
  df <- 1
}

myfunction(mydt)  # Nothing printed as expected

mydt  # Nothing printed unexpectedly

将带有尾随[]的dt之后移动df <- 1会产生预期的行为。
mydt <- data.table(x = 1:3, y = 5:7)

myfunction <- function(dt) {
  dt[, z := y - x][]
  df <- 1
}

myfunction(mydt)  # Nothing printed as expected

mydt  # Content printed as desired
##    x y z
## 1: 1 5 4
## 2: 2 6 4
## 3: 3 7 4

4
我认为你在某种程度上混淆了函数的工作原理。所有的函数都返回一个值。如果你没有编写明确的return(x)语句,那么函数中最后一个值将被返回。df <- 1以不可见的方式返回“1”这个值,而DT[, x := y][]则返回打印的DT值。 - Frank
2
谢谢你的解释。我之前没有意识到这一点。我猜是“隐式返回”的部分让我困惑了。数据表的“按引用复制”方面也让我感到困惑。我花了很长时间来尝试理解那些例子。现在你知道为什么我不在这个论坛上回答问题了 :-) - Paul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接