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

时间:2018-09-04 16:51:28

标签: r data.table

因此,如果我使用现有向量和setDT的data.frame构建data.table,则原始向量将在父环境中被修改:

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

有人可以解释我发生了什么,如果是错误的话?

欢呼

EDIT1:刚刚在github上发现了这个相关问题 https://github.com/Rdatatable/data.table/issues/2683

EDIT2:犯罪嫌疑人显然是“按引用复制”,因此向量x和dt $ x的内存地址相同,因此修改了data.table之外的向量。我本以为data.frame的创建会复制一份...

> 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 个答案:

答案 0 :(得分:3)

setDT通过引用修改输入对象。如果用作输入的对象本身是通过执行浅表复制(而不是深表复制)创建的,则将在data.table中使用:=set()时修改所有此类对象。 / p>

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

您可以通过直接创建data.tables来避免此类情况。相反,如果直接将data.frame对象提供给您,并且您不知道它是如何创建的,最好使用copy()。 HTH。