Да, это суб-назначение в 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 ). Но это означает, что манипулирование данными.таблицы не будет функционировать так же, как манипулирование фреймами данных, и, следовательно, это далеко не полная замена фрейма данных.