2013-03-04 12 views
18

Sono in fase di creazione di un pacchetto che utilizza un set di dati data.table e ha un paio di funzioni che assegnano per riferimento utilizzando :=.Assegnazione per riferimento nei set di dati del pacchetto caricato

ho costruito un pacchetto semplice per dimostrare il mio problem

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

Contiene due funzioni

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

e un set di dati (non pigro carico) DT, creati utilizzando

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

Quando installo questo pacchetto, la mia comprensione è che foo(DT) debba essere assegnato per riferimento entro 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 

Se uso il più 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) 

Se uso eval e substitute all'interno della funzione

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 

Devo restare con

  1. DT <- foo(DT) o eval/substitute route, o
  2. C'è qualcosa che non capisco su come data carica set di dati, anche quando non è pigro?
+0

mai cercato di aggiornare con i dati di riferimento nei pacchetti! Ma i dati nei pacchetti non dovrebbero essere letti solo dato che sono _sealed_? Digitare 'DT' alla fine qui non significa che sia stato assegnato per riferimento, vero? DT potrebbe essere stato copiato in '.GlobalEnv' e potrebbe essere dove è stato aggiornato. –

+0

Btw 'tracemem' riporta _duplications_ da R stesso. È improbabile che riesca a catturare una copia di data.table, ad esempio quando si esegue una sovra-allocazione per la prima volta perché tecnicamente non è una duplicazione perfetta, ma un'eccessiva allocazione (anche se una copia superficiale non è profonda). –

+0

Forse prova 'alloc.col' sull'oggetto dati nel pacchetto e vedi cosa succede. –

risposta

10

Questo non ha nulla a che fare con set di dati o di blocco - si può riprodurre semplicemente utilizzando

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

ho il sospetto che ha a che fare con il fatto che data.table deve ricreare l'extptr all'interno del oggetto al primo accesso su DT, ma lo fa su una copia quindi non c'è modo di condividere la modifica con l'originale nell'ambiente globale.


[Da Matteo] Esattamente.

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 

Oppure, non passano DT nella funzione, basta fare riferimento ad esso direttamente. Utilizzare data.table come un database: alcune tabelle di nomi fissi in .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 
+1

@Matthew: ma si noti che 'alloc.col()' ugualmente * non * funziona all'interno delle funzioni (per le stesse ragioni sopra) - è davvero necessario qualcosa che non provi a simulare riferimenti - ad es. ciò che funziona è 'DT <- DT [TRUE]'. Questo è qualcosa che vale la pena menzionare nei documenti 'data.table', perché gli oggetti unserializing' data.table' creano un problema difficile da rintracciare (e succede sempre, in spazi di lavoro, pacchetti, ecc.). –

+0

Solo un problema, credo, se è necessario aggiungere una colonna facendo riferimento a data.table unserialized, all'interno di una funzione, e il nome di tale tabella non è noto in anticipo (cioè deve essere passato attraverso la funzione discussione). Non riesco a pensare ad un esempio in cui chiamare 'alloc.col (DT)' subito dopo la unserialize non sarebbe possibile, ma necessario anche nella pratica. Tendo ad usare data.table come un database; Ad esempio alcune grandi tabelle di nomi fissi in .GlobalEnv. Si prega di vedere la nuova modifica. –

+0

@MatthewDowle (e Simon) - grazie per i suggerimenti, posso vedere che il tuo secondo esempio è in qualche modo simile alla mia idea di costruire la chiamata appropriata e di valutarla nell'ambiente genitore corretto. ('fooCall' nella mia domanda) – mnel

3

Un'altra soluzione è quella di utilizzare inst/extdata per salvare il file rda (che dovrebbe contenere un numero qualsiasi di dati.oggetti per la tavola) e hanno un file DT.r all'interno del data sottodirectory

# 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) 



} 
+0

+1 Interessante. Mi chiedo se 'alloc.col' dovrebbe essere migliorato per accettare anche un vettore di caratteri? Quindi potrebbe racchiudere la chiamata 'load()'. Non penso che tu abbia bisogno del prefisso 'data.table ::' dato che 'alloc.col' è esportato e destinato all'uso da parte dell'utente. –

+0

@MatthewDowle, buon punto re 'data.table ::' corretto e rivisto nel caso specifico in cui i risultati di 'load' sono noti in anticipo. 'alloc.col' potrebbe richiedere anche un argomento di ambiente. – mnel

+0

Buona idea. [FR # 2595] (https://r-forge.r-project.org/tracker/index.php?func=detail&aid=2595&group_id=240&atid=978) per migliorare 'alloc.col' ora archiviato. –

Problemi correlati