Saya mengalami sedikit kesulitan memahami properti pass-by-reference dari data.table
. Beberapa operasi tampaknya 'mematahkan' referensi, dan saya ingin memahami persis apa yang terjadi.
Pada membuat data.table
dari yang lain data.table
(via <-
, kemudian memperbarui tabel baru dengan :=
, tabel asli juga diubah. Ini diharapkan, sesuai:
?data.table::copy
dan stackoverflow: pass-by-referensi-operator-dalam-data-tabel-paket
Ini sebuah contoh:
library(data.table)
DT <- data.table(a=c(1,2), b=c(11,12))
print(DT)
# a b
# [1,] 1 11
# [2,] 2 12
newDT <- DT # reference, not copy
newDT[1, a := 100] # modify new DT
print(DT) # DT is modified too.
# a b
# [1,] 100 11
# [2,] 2 12
Namun, jika saya menyisipkan :=
modifikasi tidak berbasis antara <-
penugasan dan :=
baris di atas, DT
sekarang tidak lagi dimodifikasi:
DT = data.table(a=c(1,2), b=c(11,12))
newDT <- DT
newDT$b[2] <- 200 # new operation
newDT[1, a := 100]
print(DT)
# a b
# [1,] 1 11
# [2,] 2 12
Jadi sepertinya newDT$b[2] <- 200
garis entah bagaimana 'mematahkan' referensi. Saya kira ini entah bagaimana meminta salinan, tetapi saya ingin memahami sepenuhnya bagaimana R memperlakukan operasi ini, untuk memastikan saya tidak memperkenalkan bug potensial dalam kode saya.
Saya akan sangat menghargai jika seseorang dapat menjelaskan hal ini kepada saya.
sumber
<-
alih-alih=
untuk penugasan dasar di R (misalnya oleh Google: google.github.io/styleguide/Rguide.xml#assignment ). Tetapi ini berarti bahwa manipulasi data.tabel tidak akan berfungsi dengan cara yang sama seperti manipulasi bingkai data dan karenanya jauh dari penggantian drop-in ke bingkai data.Jawaban:
Ya, subassignment di R menggunakan
<-
(atau=
atau->
) yang membuat salinan seluruh objek. Anda dapat melacaknya menggunakantracemem(DT)
dan.Internal(inspect(DT))
, seperti di bawah ini. Thedata.table
fitur:=
danset()
menetapkan dengan mengacu apapun keberatan mereka berlalu. Jadi, jika objek itu sebelumnya disalin (oleh subassigning<-
atau eksplisitcopy(DT)
) maka itu adalah salinan yang akan dimodifikasi oleh referensi.Perhatikan bagaimana bahkan
a
vektor disalin (nilai heks yang berbeda menunjukkan salinan vektor baru), meskipuna
tidak diubah. Bahkan keseluruhanb
sudah disalin, bukan hanya mengubah elemen yang perlu diubah. Itu penting untuk dihindari untuk data besar, dan mengapa:=
danset()
diperkenalkandata.table
.Sekarang, dengan salinan
newDT
kami, kami dapat memodifikasinya dengan referensi:Perhatikan bahwa semua 3 nilai hex (vektor titik kolom, dan masing-masing dari 2 kolom) tetap tidak berubah. Jadi itu benar-benar dimodifikasi dengan referensi tanpa salinan sama sekali.
Atau, kita dapat memodifikasi yang asli
DT
dengan referensi:Nilai hex tersebut sama dengan nilai asli yang kami lihat di
DT
atas. Ketikexample(copy)
untuk lebih banyak contoh menggunakantracemem
dan membandingkandata.frame
.Btw, jika Anda
tracemem(DT)
makaDT[2,b:=600]
Anda akan melihat satu salinan dilaporkan. Itu adalah salinan dari 10 baris pertama yang dilakukan olehprint
metode ini. Ketika dibungkus denganinvisible()
atau ketika dipanggil dalam suatu fungsi atau skrip,print
metode tersebut tidak dipanggil.Semua ini berlaku di dalam fungsi juga; yaitu,
:=
danset()
jangan salin saat menulis, bahkan dalam fungsi. Jika Anda perlu memodifikasi salinan lokal, hubungix=copy(x)
di awal fungsi. Tetapi, yang diingatdata.table
adalah untuk data besar (juga keunggulan pemrograman lebih cepat untuk data kecil). Kami sengaja tidak ingin menyalin objek besar (pernah). Akibatnya, kita tidak perlu mengizinkan aturan praktis faktor memori 3 * yang biasa digunakan. Kami mencoba hanya perlu memori yang bekerja sebesar satu kolom (yaitu faktor memori kerja 1 / ncol daripada 3).sumber
->
penugasan yang mengubah lokasi memori. Vektor yang tidak berubah mempertahankan lokasi memori dari vektor dari data.frame asli. Perilakudata.table
s yang dijelaskan di sini adalah perilaku saat ini pada 1.12.2.Hanya ringkasan singkat.
<-
dengandata.table
seperti basis; yaitu, tidak ada salinan yang diambil sampai subassign dilakukan setelahnya dengan<-
(seperti mengubah nama kolom atau mengubah elemen sepertiDT[i,j]<-v
). Kemudian dibutuhkan salinan seluruh objek seperti basis. Itu dikenal sebagai copy-on-write. Akan lebih dikenal sebagai copy-on-subassign, saya pikir! JANGAN menyalin ketika Anda menggunakan:=
operator khusus , atauset*
fungsi yang disediakan olehdata.table
. Jika Anda memiliki data besar, Anda mungkin ingin menggunakannya.:=
danset*
TIDAK akan menyalindata.table
, BAHKAN DALAM FUNGSI.Diberikan contoh data ini:
Berikut ini hanya "mengikat" nama lain
DT2
ke objek data yang sama terikat saat ini terikat pada namaDT
:Ini tidak pernah menyalin, dan tidak pernah menyalin di pangkalan juga. Itu hanya menandai objek data sehingga R tahu bahwa dua nama berbeda (
DT2
danDT
) menunjuk ke objek yang sama. Maka R perlu menyalin objek jika salah satunya ditransfer ke bawah sesudahnya.Itu sempurna untuk
data.table
juga. The:=
bukan untuk melakukan hal itu. Jadi berikut ini adalah kesalahan yang disengaja karena:=
bukan untuk hanya mengikat nama objek::=
adalah untuk subassigning dengan referensi. Tetapi Anda tidak menggunakannya seperti pada basis:Anda menggunakannya seperti ini:
Itu berubah
DT
dengan referensi. Katakanlah Anda menambahkan kolom barunew
dengan referensi ke objek data, tidak perlu melakukan ini:karena RHS sudah diubah
DT
dengan referensi. KelebihannyaDT <-
adalah salah paham apa yang:=
dilakukannya. Anda dapat menulisnya di sana, tetapi itu berlebihan.DT
diubah dengan referensi, oleh:=
, BAHKAN DALAM FUNGSI:data.table
untuk dataset besar, ingat. Jika Anda memilikidata.table
memori 20GB maka Anda perlu cara untuk melakukan ini. Ini adalah keputusan desain yang sangat disengajadata.table
.Salinan dapat dibuat, tentu saja. Anda hanya perlu memberi tahu data.tabel bahwa Anda yakin ingin menyalin dataset 20GB Anda, dengan menggunakan
copy()
fungsi:Untuk menghindari salinan, jangan gunakan penetapan atau pembaruan tipe dasar:
Jika Anda ingin memastikan bahwa Anda memperbarui dengan menggunakan referensi
.Internal(inspect(x))
dan melihat nilai-nilai alamat memori konstituen (lihat jawaban Matthew Dowle).Menulis
:=
dengan caraj
seperti itu memungkinkan Anda melakukan subassign dengan referensi berdasarkan grup . Anda dapat menambahkan kolom baru dengan referensi berdasarkan grup. Jadi itu sebabnya:=
dilakukan seperti itu di dalam[...]
:sumber