Так, саме підрозділ 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 / нкол, а не 3).
<-замість=основного завдання в R (наприклад, Google: google.github.io/styleguide/Rguide.xml#assignment ). Але це означає, що маніпуляція з data.table не буде функціонувати так само, як маніпулювання кадрами даних, і тому далеко не заміна заміни для кадру даних.