はい、オブジェクト全体のコピーを作成するのは、<-
(または=
または)を使用した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
変更されていない場合でも、ベクトルがどのようにコピーされたか(異なる16進値はベクトルの新しいコピーを示します)に注意してください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つの16進値すべて(列ポイントのベクトル、および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..
これらの16進値は、DT
上記で見た元の値と同じです。example(copy)
を使用して他の例を入力しtracemem
、と比較しdata.frame
ます。
ところで、あなたがそうするならtracemem(DT)
、DT[2,b:=600]
あなたは1つのコピーが報告されるのを見るでしょう。print
これは、メソッドが実行する最初の10行のコピーです。関数またはスクリプトでラップされたinvisible()
場合、または関数またはスクリプト内で呼び出された場合、print
メソッドは呼び出されません。
これはすべて関数内にも当てはまります。つまり、関数内であっても、書き込み時にコピー:=
しset()
ないでください。ローカルコピーを変更する必要がある場合x=copy(x)
は、関数の開始時に呼び出します。ただし、大きなデータの場合は覚えておいてdata.table
ください(また、小さなデータの場合はプログラミングの利点が速くなります)。意図的に大きなオブジェクトをコピーしたくありません(これまで)。結果として、通常の3*ワーキングメモリファクターの経験則を考慮する必要はありません。1列の作業メモリーのみが必要になるようにします(つまり、作業メモリー係数は3ではなく1 / ncolです)。