为什么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" 

我的问题是双重的:

  1. 为什么data.table names1 <- names(DT)不会创builddata.table名称的副本? 在其他情况下,我们被明确警告: <-创builddata.tabledata.frame的副本。
  2. names2 <- c(names(DT)) names1 <- names(DT)names2 <- c(names(DT))之间有什么区别?

更新:现在在1.9.3版本的文档中添加了这个function。 来自NEWS :

  1. 移动?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 names1truelength是100(这是默认分配在data.table ,检查?alloc.col为此)。

 truelength(names1) # [1] 100 

所以基本上,赋值names1 <- names(dt)似乎是通过引用发生的。 也就是说, names1指向与dt的列名指针相同的位置。

回答你的第二个问题:命令c(.)似乎创build一个副本, 因为不检查由于连接操作导致的内容结果是否不同 。 也就是说,因为c(.)操作可以改变向量的内容,所以立即导致“复制” 而不检查内容是否被修改。