首页 > 解决方案 > R data.table 奇怪的值/引用语义

问题描述

(这是对此的后续问题。)

检查这个玩具代码:

> x <- data.frame(a = 1:2)
> foo <- function(z) { setDT(z) ; z[, b:=3:4] ; z } 
> y <- foo(x)
> 
> class(x)
[1] "data.table" "data.frame"
> x
   a
1: 1
2: 2

看起来 setDT 确实改变了 x 的类,但添加的数据不适用于 x。
这里发生了什么?

标签: rdata.table

解决方案


在您的函数中是对untilz的引用。xsetDT

library(data.table)
foo <- function(z) {print(address(z)); setDT(z); print(address(z))} 
x <- data.frame(a = 1:2)
address(x)
#[1] "0x555ec9a471e8"
foo(x)
#[1] "0x555ec9a471e8"
#[1] "0x555ec9ede300"

setDT下面的行中,z它仍然指向相同的地址,例如x

setattr(z, "class", data.table:::.resetclass(z, "data.frame"))

setattr不制作副本。所以xandz仍然指向同一个地址,并且现在都属于类data.frame

x <- data.frame(a = 1:2)
z <- x
class(x)
#[1] "data.frame"
address(x)
#[1] "0x555ec95de600"
address(z)
#[1] "0x555ec95de600"

setattr(z, "class", data.table:::.resetclass(z, "data.frame"))

class(x)
#[1] "data.table" "data.frame"
address(x)
#[1] "0x555ec95de600"
address(z)
#[1] "0x555ec95de600"

然后setalloccol调用 which 在这种情况下调用:

assign("z", .Call(data.table:::Calloccolwrapper, z, 1024, FALSE))

现在让xz指向不同的地址。

address(x)
#[1] "0x555ecaa09c00"
address(z)
#[1] "0x555ec95de600"

两者都有class data.frame

class(x)
#[1] "data.table" "data.frame"
class(z)
#[1] "data.table" "data.frame"

我想他们什么时候会用

class(z) <- data.table:::.resetclass(z, "data.frame")

代替

setattr(z, "class", data.table:::.resetclass(z, "data.frame"))

问题不会发生。

x <- data.frame(a = 1:2)
z <- x
address(x)
#[1] "0x555ec9cd2228"
class(z) <- data.table:::.resetclass(z, "data.frame")
class(x)
#[1] "data.frame"
class(z)
#[1] "data.table" "data.frame"
address(x)
#[1] "0x555ec9cd2228"
address(z)
#[1] "0x555ec9cd65a8"

但 afterclass(z) <- value z不会指向它之前指向的相同地址:

z <- data.frame(a = 1:2)
address(z)
#[1] "0x5653dbe72b68"
address(z$a)
#[1] "0x5653db82e140"
class(z) <- c("data.table", "data.frame")
address(z)
#[1] "0x5653dbe82d98"
address(z$a)
#[1] "0x5653db82e140"

但之后setDT它也不会指向它之前指向的相同地址:

z <- data.frame(a = 1:2)
address(z)
#[1] "0x55b6f04d0db8"
setDT(z)
address(z)
#[1] "0x55b6efe1e0e0"

正如@Matt-dowle 指出的那样,也可以更改xover中的数据z

x <- data.frame(a = c(1,3))
z <- x
setDT(z)
z[, b:=3:4]
z[2, a:=7]
z
#   a b
#1: 1 3
#2: 7 4
x
#   a
#1: 1
#2: 7
R.version.string
#[1] "R version 4.0.2 (2020-06-22)"
packageVersion("data.table")
#[1] ‘1.12.8’

推荐阅读