【问题标题】:data.table assignment by reference modifies wrong objectdata.table 通过引用分配修改了错误的对象
【发布时间】:2019-12-06 05:06:48
【问题描述】:

在对 data.table 中的列进行分组修改时,我遇到了一些意外行为:

# creating a data.frame
data <- data.frame(sequence = rep(c("A","B","C","D"), c(2,3,3,2)), trim = 0, random_value = NA)
data[c(1:4, 10), "trim"] <- 1

# copying data to data_temp
data_temp <- data

# assigning some random value to data_temp so that it should no longer be a
# copy of "data"
data_temp[1, "random_value"] <- rnorm(1)

# converting data_temp to data.table
setDT(data_temp)

# expanding trim parameter to group and subsetting
data_temp <- data_temp[, trim := sum(trim), by = sequence][trim == 0]

data_temp 按预期出现,只剩下“C”序列条目。但是,我也希望“数据”对象保持不变。不是这种情况。 “数据”对象如下所示:

   sequence trim random_value
1         A    2           NA
2         A    2           NA
3         B    2           NA
4         B    2           NA
5         B    2           NA
6         C    0           NA
7         C    0           NA
8         C    0           NA
9         D    1           NA
10        D    1           NA

所以“trim”变量的引用赋值也发生在原始data.frame中。

出于兼容性原因,我正在使用 data.table_1.11.4 和 R 版本 3.4.3。

错误是使用旧版本导致的还是我做错了什么/我是否需要更改代码以避免该错误?

【问题讨论】:

  • 阅读help("copy")
  • 啊,谢谢。很高兴知道,如果我复制的对象实际上不是 data.table 对象而是 data.frames,也有必要使用 copy(),其中只有一个稍后会成为 data.table。
  • @Roland 我很惊讶地看到data_temp[1, "random_value"] &lt;- rnorm(1) 没有复制整个data.frame,而只是复制了“random_value”向量。因此,在这一行之后,单独的 data.frames 的 sequence 和 trim 变量仍然指向内存中的相同对象。我用.Internal(inspect(.)) 验证了这一点。我想知道这种行为在基础 R 中的默认行为有多长时间了。也许是因为允许列表保存指针?
  • @大卫。目前还不清楚这是一个重复的问题。尽管“在做任何事情之前创建副本”的建议可以解决这两个问题,但 -&gt; 的复制行为对于 data.frame 和 data.table 对象是不同的。您可以通过使用 data.framea 重复 matt dowle 的示例并检查向量的内存位置来看到这一点。这将更准确地反映上述情况。

标签: r data.table


【解决方案1】:

正如@Roland 在他对原始问题的评论中亲切地指出的那样,有必要使用“copy()”函数显式复制 data.table 中的对象。否则 data.table 不会将复制的对象视为不同的对象,并将修改两个对象中具有相同名称的列。正如@Imo 检查的那样,只有在两个data.frames 之一中更改的列而不是通过引用(例如示例中的“random_value”)实际上被复制/取消链接。

使用 copy() 函数可以轻松解决此问题:

# creating a data.frame
data <- data.frame(sequence = rep(c("A","B","C","D"), c(2,3,3,2)), trim = 0, random_value = NA)
data[c(1:4, 10), "trim"] <- 1

# copying data to data_temp explicitly
data_temp <- copy(data)

# assigning some random value to data_temp so that it should no longer be a
# copy of "data" - if the copy() function isn't used, that just unlinks the 
# "random_value" column, but not the others
data_temp[1, "random_value"] <- rnorm(1)

# converting data_temp to data.table
setDT(data_temp)

# expanding trim parameter to group and subsetting
data_temp <- data_temp[, trim := sum(trim), by = sequence][trim == 0]

因此,每次您不希望 data.table 对复制表进行的引用修改影响原始表时,都必须使用 copy() 函数(反之亦然) - 即使在您复制表时也是如此它们(还)不是 data.table 类对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-08-16
    • 2018-04-19
    • 2019-12-29
    相关资源
    最近更新 更多