为什么data.table通过引用更新名称(DT),即使我分配给另一个变量?

Zac*_*ach 40 r data.table

我已将a的名称存储data.tablevector:

library(data.table)
set.seed(42)
DT <- data.table(x = runif(100), y = runif(100))
names1 <- names(DT)
Run Code Online (Sandbox Code Playgroud)

据我所知,这是一个普通的香草字符向量:

str(names1)
# chr [1:2] "x" "y"

class(names1)
# [1] "character"

dput(names1)
# c("x", "y")
Run Code Online (Sandbox Code Playgroud)

但是,这不是普通的字符向量.这是一个神奇的角色矢量!当我向我添加一个新列时data.table,此向量会更新!

DT[ , z := runif(100)]
names1
# [1] "x" "y" "z"
Run Code Online (Sandbox Code Playgroud)

我知道这与如何:=通过作业进行更新有关,但这对我来说仍然是神奇的,因为我希望<-能够复制这些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"
Run Code Online (Sandbox Code Playgroud)

我的问题是双重的:

  1. 为什么不names1 <- names(DT)创建这些data.table名字的副本?在其他情况下,我们被明确警告,<-创建副本,data.tables和data.frames.
  2. names1 <- names(DT)和之间有什么区别names2 <- c(names(DT))

Aru*_*run 40

更新:现在已?copy在1.9.3版的文档中添加.来自新闻:

  1. 移动?copy到它自己的帮助页面,并记录了由于通过引用更新而未被引用修改dt_names <- copy(names(DT))所必需的(例如:通过引用添加新列).关闭#512.感谢Zach提出的这个问题,以及针对此问题的 user1971988 .dt_namesDT

你的第一个问题的一部分使对你对<-运算符的真正含义(至少在上下文中data.table)有点不清楚,尤其是部分:在其他情况下,我们明确警告< - 创建副本,两者都是数据.tables和data.frames.

所以,在回答你的实际问题之前,我会在这里简单地触摸一下.如果data.tablea <-(赋值)仅仅不足以复制a 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
Run Code Online (Sandbox Code Playgroud)

如果你想创建一个copy,那么你要使用copy命令明确提到它.

DT2 <- copy(DT) # copied content to DT2
DT2[, z := 11:15] # only DT2 is affected
Run Code Online (Sandbox Code Playgroud)

从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"
Run Code Online (Sandbox Code Playgroud)

在这里,您看到它们指向相同的内存位置@7fc86a851480.连truelengthnames1是100(这是由分配在默认情况下data.table,检查?alloc.col了这一点).

truelength(names1)
# [1] 100
Run Code Online (Sandbox Code Playgroud)

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

回答你的第二个问题:该命令c(.)似乎创建了一个副本,因为没有检查由于串联操作导致的内容结果是否不同.也就是说,因为c(.)操作可以改变向量的内容,所以它立即导致进行"复制" 而不检查内容是否被修改.

  • 这对我来说也是个新闻,很棒的问题.名称向量被过度分配以及列指针的向量.当`:=`指定一个新列时,它也会通过引用更新名称向量.这可以通过不使名称向量过度分配来改变/修复.但是如果在循环中添加列,则会慢一点.因此,或许更好地记录需要`nm < - copy(names(DT))`,否则`names(DT)`返回一个引用. (6认同)