Да, это суб-назначение в R с использованием <-(или =или ->), которое делает копию всего объекта. Вы можете отследить это, используя tracemem(DT)и .Internal(inspect(DT)), как показано ниже. В data.tableособенности :=и set()правопреемник по отношению к любому объекту , они передаются. Таким образом, если этот объект был ранее скопирован (с помощью суб- <-присвоения или явного copy(DT)), то это копия, которая модифицируется по ссылке.
DT <- data.table(a = c(1, 2), b = c(11, 12))
newDT <- DT
.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB: # ..snip..
.Internal(inspect(newDT)) # precisely the same object at this point
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB: # ..snip..
tracemem(newDT)
# [1] "<0x0000000003b7e2a0"
newDT$b[2] <- 200
# tracemem[0000000003B7E2A0 -> 00000000040ED948]:
# tracemem[00000000040ED948 -> 00000000040ED830]: .Call copy $<-.data.table $<-
.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),TR,ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,12
# ATTRIB: # ..snip..
.Internal(inspect(newDT))
# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,200
# ATTRIB: # ..snip..
Обратите внимание, что даже aвектор был скопирован (другое шестнадцатеричное значение указывает на новую копию вектора), даже если aон не был изменен. Даже все bкопирование было скопировано, а не просто изменены элементы, которые необходимо изменить. Это важно, чтобы избежать для больших данных, и почему :=и set()были представлены data.table.
Теперь, с нашим скопированным, newDTмы можем изменить его по ссылке:
newDT
# a b
# [1,] 1 11
# [2,] 2 200
newDT[2, b := 400]
# a b # See FAQ 2.21 for why this prints newDT
# [1,] 1 11
# [2,] 2 400
.Internal(inspect(newDT))
# @0000000003D97A58 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040ED7F8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040ED8D8 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,400
# ATTRIB: # ..snip ..
Обратите внимание, что все 3 шестнадцатеричных значения (вектор точек столбцов и каждый из 2 столбцов) остаются неизменными. Так что он был действительно изменен по ссылке без копий вообще.
Или мы можем изменить оригинал DTпо ссылке:
DT[2, b := 600]
# a b
# [1,] 1 11
# [2,] 2 600
.Internal(inspect(DT))
# @0000000003B7E2A0 19 VECSXP g0c7 [OBJ,NAM(2),ATT] (len=2, tl=100)
# @00000000040C2288 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 1,2
# @00000000040C2250 14 REALSXP g0c2 [NAM(2)] (len=2, tl=0) 11,600
# ATTRIB: # ..snip..
Эти шестнадцатеричные значения совпадают с исходными значениями, которые мы видели DTвыше. Введите example(copy)дополнительные примеры с использованием tracememи сравнение с data.frame.
Кстати, если вы, tracemem(DT)то DT[2,b:=600]вы увидите одну копию сообщили. Это копия первых 10 строк, которые printделает метод. При переносе invisible()или при вызове внутри функции или скрипта printметод не вызывается.
Все это относится и к внутренним функциям; т.е. :=и set()не копировать при записи даже внутри функций. Если вам нужно изменить локальную копию, вызовите ее x=copy(x)в начале функции. Но помните, data.tableчто для больших данных (а также преимущества быстрого программирования для небольших данных). Мы сознательно не хотим копировать большие объекты (никогда). В результате нам не нужно учитывать обычное эмпирическое правило с 3 * рабочим коэффициентом памяти. Мы стараемся использовать рабочую память размером не более одного столбца (т.е. коэффициент рабочей памяти равен 1 / ncol, а не 3).
<-вместо=базового назначения в R (например, Google: google.github.io/styleguide/Rguide.xml#assignment ). Но это означает, что манипулирование данными.таблицы не будет функционировать так же, как манипулирование фреймами данных, и, следовательно, это далеко не полная замена фрейма данных.