为什么data.table通过引用更新名称(DT),即使我分配给另一个variables?
我已经将data.table
的名称存储为一个vector
:
library(data.table) set.seed(42) DT <- data.table(x = runif(100), y = runif(100)) names1 <- names(DT)
据我所知,这是一个普通的香草字符vector:
str(names1) # chr [1:2] "x" "y" class(names1) # [1] "character" dput(names1) # c("x", "y")
但是,这不是普通的字符向量。 这是一个魔法字符vector! 当我添加一个新的列到我的data.table
,这个向量得到更新!
DT[ , z := runif(100)] names1 # [1] "x" "y" "z"
我知道这与如何处理:=
通过赋值更新有关,但是对于我来说,这仍然看起来很神奇,因为我期望<-
将data.table
的名称复制一份 。
我可以通过在c()
包装名称来解决这个问题:
library(data.table) set.seed(42) DT <- data.table(x = runif(100), y = runif(100)) names1 <- names(DT) names2 <- c(names(DT)) all.equal(names1, names2) # [1] TRUE DT[ , z := runif(100)] names1 # [1] "x" "y" "z" names2 # [1] "x" "y"
我的问题是双重的:
- 为什么
data.table
names1 <- names(DT)
不会创builddata.table
名称的副本? 在其他情况下,我们被明确警告:<-
创builddata.table
和data.frame
的副本。 -
names2 <- c(names(DT))
names1 <- names(DT)
和names2 <- c(names(DT))
之间有什么区别?
更新:现在在1.9.3版本的文档中添加了这个function。 来自NEWS :
- 移动
?copy
到它自己的帮助页面,并loggingdt_names <- copy(names(DT))
是必要的dt_names
不作为引用修改作为引用更新DT
的结果(例如:通过引用添加一个新的列) 。 closures#512 。 感谢扎克这个SO问题和user1971988 这个SO问题 。
你的第一个问题的一部分使我不清楚你真正的意思是什么<-
操作符(至less在data.table
的情况下),特别是部分: 在其他情况下,我们被明确警告,< – 创build副本,data.tables和data.frames。
所以,在回答你真正的问题之前,我会在这里简单地谈谈它。 在data.table
的情况下, <-
(赋值)仅仅是不足以复制data.table
。 例如:
DT <- data.table(x = 1:5, y= 6:10) # assign DT2 to DT DT2 <- DT # assign by reference, no copy taken. DT2[, z := 11:15] # DT will also have the z column
如果你想创build一个copy
,那么你必须使用copy
命令明确提到它。
DT2 <- copy(DT) # copied content to DT2 DT2[, z := 11:15] # only DT2 is affected
从CauchyDistributedRV,我明白你的意思是分配names(dt) <- .
这将导致警告。 我会离开它。
现在,回答你的第一个问题: names1 <- names(DT)
似乎也是相似的。 直到现在我还没有想到/知道这个。 .Internal(inspect(.))
命令在这里非常有用:
.Internal(inspect(names1)) # @7fc86a851480 16 STRSXP g0c7 [MARK,NAM(2)] (len=2, tl=100) # @7fc86a069f68 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "x" # @7fc86a0f96d8 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "y" .Internal(inspect(names(DT))) # @7fc86a851480 16 STRSXP g0c7 [MARK,NAM(2)] (len=2, tl=100) # @7fc86a069f68 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "x" # @7fc86a0f96d8 09 CHARSXP g1c1 [MARK,gp=0x61] [ASCII] [cached] "y"
在这里,你看到他们指向相同的内存位置@7fc86a851480
。 即使truelength
names1
的truelength
是100(这是默认分配在data.table
,检查?alloc.col
为此)。
truelength(names1) # [1] 100
所以基本上,赋值names1 <- names(dt)
似乎是通过引用发生的。 也就是说, names1
指向与dt的列名指针相同的位置。
回答你的第二个问题:命令c(.)
似乎创build一个副本, 因为不检查由于连接操作导致的内容结果是否不同 。 也就是说,因为c(.)
操作可以改变向量的内容,所以立即导致“复制” 而不检查内容是否被修改。