data.table修改父环境/使用setDT时出现的奇怪行为

6

如果我使用现有向量的数据框构建数据表,并使用setDT函数,那么原始向量会在父环境中修改:

a <- 1:2 / 2
x <- 1:10 / 2
y <- 11/2
dt <- data.frame(a, x, y)
setDT(dt)
dt[ , cond := a == 1]
dt[(cond), c("x", "y") := list(y, x)]
x
#[1] 0.5 5.5 1.5 5.5 2.5 5.5 3.5 5.5 4.5 5.5

补充信息:我使用的是R 3.5.1版本和data.table 1.11.4版本。

如果我使用 data.table 的构造函数而不是 data.frame + setDT,它不会修改向量 x。

a <- 1:2 / 2
x <- 1:10 / 2
y <- 11/2
dt <- data.table(a, x, y)
dt[ , cond := a == 1]
dt[(cond), c("x", "y") := list(y, x)]
x
#[1] 0.5 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0

有人能给我解释一下发生了什么事吗?这是否是一个错误?
编辑1:我在github上找到了相关问题 https://github.com/Rdatatable/data.table/issues/2683 编辑2:嫌疑人显然是“按引用复制”,因此向量x和dt$x的内存地址相同,因此它会修改数据表外部的向量。我认为数据框创建应该会进行复制...
> a <- 1:2 / 2
> x <- 1:10 / 2
> y <- 11/2
> dt <- setDT(as.data.frame(list(a = a, x = x, y = y)))
> dt[ , cond := a == 1]
> dt[(cond), c("x", "y") := list(y, x)]
> x
[1] 0.5 5.5 1.5 5.5 2.5 5.5 3.5 5.5 4.5 5.5
> address(dt$x)
[1] "0xadd8fe8"
> address(x)
[1] "0xadd8fe8"

1
同时也很奇怪(我认为),如果 xy 是整数 (x <- 1:10L; y <- 11L),那么原始的 x 不会被修改! - Jeff
@Jeff 我尝试了你的例子,我也看到 x 在那里被修改了。顺便说一下,我有时会使用这个“特性”,例如 z = 1:3; setDT(list(z))[2, V1 := 99L][]; z。我认为它以前已经被归档为一个 bug,但现在我在问题跟踪器上找不到它了。 - Frank
1个回答

8

setDT 通过引用修改输入对象。如果作为输入的对象本身是通过执行浅复制而创建的(而不是深复制),则在使用:=set()时,所有这些对象都将被修改。

data.frame() 似乎会在创建时尽可能地创建输入对象的浅表副本以提高效率。因此,address(df$x)address(x) 是相同的。这是可以接受的,因为R执行的是复制-修改操作。

您可以通过直接创建data.table来避免这种情况。如果给你的是一个data.frame对象,而你对它的创建方式一无所知,最好使用copy()。希望有所帮助。


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