Подтвердить что ты не робот

Почему имена обновления data.table(DT) по ссылке, даже если я назначаю другую переменную?

Я сохранил имена data.table как vector:

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

Насколько я могу судить, это простой вектор символа ванили:

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

class(names1)
# [1] "character"

dput(names1)
# c("x", "y")

Однако это не обычный вектор символов. Это волшебный вектор символов! Когда я добавляю новый столбец в мой 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"

Мой вопрос в 2 раза:

  • Почему names1 <- names(DT) не создает копию имен data.table? В других случаях мы явно предупреждаем, что <- создает копии, как data.table, так и data.frame s.
  • В чем разница между names1 <- names(DT) и names2 <- c(names(DT))?
4b9b3361

Ответ 1

Обновление: теперь это добавлено в документацию для ?copy в версии 1.9.3. Из новостей:

  1. Перемещено ?copy на свою страницу справки и задокументируйте, что dt_names <- copy(names(DT)) необходимо, чтобы dt_names не были изменены ссылкой в результате обновления DT по ссылке (например: добавление нового столбца по ссылке), Закрывает # 512. Спасибо Заху за этот вопрос и пользователь1971988 за этот вопрос.

Часть вашего первого вопроса немного меня нечетко говорит о том, что вы действительно имеете в виду о <- operator (по крайней мере, в контексте data.table), особенно в части: В других случаях мы явно предупреждаем, что <- создает копии, как data.tables, так и data.frames.

Поэтому, прежде чем ответить на ваш реальный вопрос, я кратко коснусь его здесь. В случае data.table 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

Если вы хотите создать copy, вы должны явно указать ее с помощью команды copy.

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

Из CauchyDistributedRV я понимаю, что вы имеете в виду names(dt) <-. присваивания 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 равна 100 (что по умолчанию выделено в data.table, для этого data.table проверить ?alloc.col).

truelength(names1)
# [1] 100

Таким образом, names1 <- names(dt) присваивания1 names1 <- names(dt) видимому, происходят по ссылке. То есть names1 указывают на то же место, что и указатель имен столбцов dt.

Чтобы ответить на ваш второй вопрос: команда c(.) Похоже, создает копию, поскольку нет проверки того, что результат содержимого из-за операции конкатенации различен. То есть, поскольку операция c(.) Может изменять содержимое вектора, это немедленно приводит к тому, что выполняется "копия", не проверяя, не изменяется ли содержимое.