menugaskan dengan referensi ke dalam kumpulan data paket yang dimuat

Saya sedang dalam proses membuat paket yang menggunakan data.table sebagai kumpulan data dan memiliki beberapa fungsi yang ditetapkan berdasarkan referensi menggunakan :=.

Saya telah membuat paket sederhana untuk mendemonstrasikan problem saya

 library(devtools)
 install_github('foo','mnel')

Ini berisi dua fungsi

foo <- function(x){
  x[, a := 1]
}
fooCall <- function(x){
  eval(substitute(x[, a :=1]),parent.frame(1))
} 

dan kumpulan data (bukan pemuatan lambat) DT, dibuat menggunakan

DT <- data.table(b = 1:5)
save(DT, file = 'data/DT.rda')

Ketika saya menginstal paket ini, pemahaman saya adalah bahwa foo(DT) harus ditetapkan dengan referensi dalam DT.

 library(foo)
 data(DT)
 foo(DT)
   b a
1: 1 1
2: 2 1
3: 3 1
4: 4 1
5: 5 1

# However this has not assigned by reference within `DT`

DT
   b
1: 1
2: 2
3: 3
4: 4
5: 5

Jika saya menggunakan lebih banyak correct

tracmem(DT)
DT <- foo(DT)
# This works without copying
DT 
 b a
1: 1 1
2: 2 1
3: 3 1
4: 4 1
5: 5 1
untracemem(DT)

Jika saya menggunakan eval dan substitute dalam fungsinya

fooCall(DT)
   b a
1: 1 1
2: 2 1
3: 3 1
4: 4 1
5: 5 1
# it does assign by reference 
DT
   b a
1: 1 1
2: 2 1
3: 3 1
4: 4 1
5: 5 1

Haruskah saya bertahan

  1. DT <- foo(DT) atau rute eval/substitute, atau
  2. Apakah ada sesuatu yang saya tidak mengerti tentang cara data memuat kumpulan data, meskipun tidak malas?

person mnel    schedule 04.03.2013    source sumber
comment
Jangan pernah mencoba memperbarui dengan data referensi dalam paket! Namun bukankah data dalam paket seharusnya hanya dibaca karena tersegel? Mengetik DT di akhir sini tidak berarti itu ditetapkan berdasarkan referensi bukan? DT bisa saja disalin ke .GlobalEnv dan mungkin di situlah DT diperbarui.   -  person Matt Dowle    schedule 04.03.2013
comment
Ngomong-ngomong, tracemem melaporkan duplikasi oleh R sendiri. Kecil kemungkinannya untuk menangkap salinan yang data.table lakukan, misalnya saat melakukan alokasi berlebihan untuk pertama kalinya karena secara teknis itu bukan duplikasi sempurna, namun alokasi berlebihan (walaupun salinan dangkal bukan salinan dalam).   -  person Matt Dowle    schedule 04.03.2013
comment
Mungkin coba alloc.col pada objek data dalam paket, dan lihat apa yang terjadi.   -  person Matt Dowle    schedule 04.03.2013
comment
@MatthewDowle Saya pikir data(DT) membuat salinan di lingkungan global, pemuatan lambat mungkin menyiratkan kumpulan data yang terkunci. Saya tidak mencoba memperbarui salinan dalam paket, tetapi menggunakan kumpulan data dalam contoh/sketsa.   -  person mnel    schedule 04.03.2013
comment
Saya tidak familiar dengan data() tapi ya, kedengarannya tidak salah. Tetapi R yang membuatnya (bukan data.tabel) yaitu perintah data() R yang tidak mengetahui tentang alokasi berlebihan. Mirip dengan saat Anda load() sebuah data.tabel, itu tidak akan dialokasikan secara berlebihan sampai := pertama menambahkan kolom baru. Apakah library(foo); data(DT); alloc.col(DT); foo(DT) berfungsi? Lalu kita bisa pergi dari sana.   -  person Matt Dowle    schedule 04.03.2013
comment
@MatthewDowle, seperti disebutkan di bawah library(foo); data(DT); alloc.col(DT); foo(DT) berfungsi sesuai kebutuhan.   -  person mnel    schedule 05.03.2013


Jawaban (2)


Ini tidak ada hubungannya dengan kumpulan data atau penguncian -- Anda dapat mereproduksinya hanya dengan menggunakan

DT<-unserialize(serialize(data.table(b = 1:5),NULL))
foo(DT)
DT

Saya menduga ini ada hubungannya dengan fakta bahwa data.table harus membuat ulang extptr di dalam objek pada akses pertama di DT, tetapi ia melakukannya pada salinan sehingga tidak ada cara ia dapat membagikan modifikasi dengan aslinya di lingkungan global.


[Dari Matthew] Tepat sekali.

DT<-unserialize(serialize(data.table(b = 1:3),NULL))
DT
   b
1: 1
2: 2
3: 3
DT[,newcol:=42]
DT                 # Ok. DT rebound to new shallow copy (when direct)
   b newcol
1: 1     42
2: 2     42
3: 3     42

DT<-unserialize(serialize(data.table(b = 1:3),NULL))
foo(DT)
   b a
1: 1 1
2: 2 1
3: 3 1
DT                 # but not ok when via function foo()
   b
1: 1
2: 2
3: 3


DT<-unserialize(serialize(data.table(b = 1:3),NULL))
alloc.col(DT)      # alloc.col needed first
   b
1: 1
2: 2
3: 3
foo(DT)
   b a
1: 1 1
2: 2 1
3: 3 1
DT                 # now it's ok
   b a
1: 1 1
2: 2 1
3: 3 1

Atau, jangan meneruskan DT ke dalam fungsi tersebut, cukup rujuk secara langsung. Gunakan data.table seperti database: beberapa tabel nama tetap di .GlobalEnv.

DT <- unserialize(serialize(data.table(b = 1:5),NULL))
foo <- function() {
   DT[, newcol := 7]
}
foo()
   b newcol
1: 1      7
2: 2      7
3: 3      7
4: 4      7
5: 5      7
DT              # Unserialized data.table now over-allocated and updated ok.
   b newcol
1: 1      7
2: 2      7
3: 3      7
4: 4      7
5: 5      7
person Simon Urbanek    schedule 04.03.2013
comment
@Matthew: tetapi perhatikan bahwa alloc.col() sama tidak akan berfungsi di dalam fungsi (untuk alasan yang sama di atas) - Anda benar-benar memerlukan sesuatu yang tidak mencoba memalsukan referensi - mis. yang berhasil adalah DT <- DT[TRUE]. Ini adalah sesuatu yang layak disebutkan dalam data.table dokumen, karena membatalkan serialisasi objek data.table menciptakan masalah yang sulit dilacak (dan ini terjadi sepanjang waktu - di ruang kerja, paket, dll.). - person Simon Urbanek; 05.03.2013
comment
Saya yakin, hanya masalah jika kolom perlu ditambahkan dengan referensi ke data.tabel yang tidak diserialisasi, dari dalam suatu fungsi, dan nama tabel tersebut tidak diketahui sebelumnya (yaitu perlu diteruskan melalui argumen fungsi ). Saya tidak dapat memikirkan contoh di mana memanggil alloc.col(DT) langsung setelah unserialize tidak mungkin dilakukan, tetapi diperlukan dalam praktiknya juga. Saya cenderung menggunakan data.table seperti database; yaitu beberapa tabel nama tetap besar di .GlobalEnv. Silakan lihat hasil edit baru. - person Matt Dowle; 05.03.2013
comment
@MatthewDowle (dan Simon) -- terima kasih atas petunjuknya, saya dapat melihat bahwa contoh kedua Anda agak mirip dengan gagasan saya dalam membuat panggilan yang sesuai, dan mengevaluasinya di lingkungan induk yang benar. (fooCall dalam pertanyaan saya) - person mnel; 05.03.2013

Solusi lain adalah dengan menggunakan inst/extdata untuk menyimpan file rda (yang akan berisi sejumlah objek data.table) dan memiliki file DT.r di dalam subdirektori data

# get the environment from the call to `data()`
env <- get('envir', parent.frame(1))
# load the data
load(system.file('extdata','DT.rda', package= 'foo'), envir = env)
# overallocate (evaluating in correct environment)
if(require(data.table)){
# the contents of `DT.rda` are known, so write out in full
  evalq(alloc.col(DT), envir = env)

}
# clean up so `env` object not present in env environment after calling `data(DT)`
rm(list = c('env'), envir = env)



}
person mnel    schedule 05.03.2013
comment
+1 Menarik. Saya ingin tahu apakah alloc.col harus ditingkatkan untuk menerima vektor karakter juga? Kemudian itu bisa mengakhiri panggilan load(). Saya rasa Anda tidak memerlukan awalan data.table:: karena alloc.col diekspor dan dimaksudkan untuk digunakan pengguna. - person Matt Dowle; 05.03.2013
comment
@MatthewDowle, poin bagus data.table:: diperbaiki, dan direvisi untuk kasus tertentu di mana hasil load diketahui sebelumnya. alloc.col mungkin memerlukan argumen lingkungan juga. - person mnel; 06.03.2013
comment
Ide bagus. FR#2595 hingga tingkatkan alloc.col sekarang diajukan. - person Matt Dowle; 06.03.2013