2016-01-18 6 views
17

Anche se avevo letto da qualche parte (non ricordo dove) quei fattori non erano effettivamente più efficienti dei vettori di caratteri in data.table. È vero? Stavo discutendo se continuare a utilizzare i fattori per memorizzare vari vettori in data.table. Un test approssimativo con object.size sembra indicare il contrario.I fattori sono memorizzati in modo più efficiente in data.table dei caratteri?

chars <- data.table(a = sample(letters, 1e5, TRUE))   # chars (not really) 
string <- data.table(a = sample(state.name, 1e5, TRUE))  # strings 
fact <- data.table(a = factor(sample(letters, 1e5, TRUE))) # factor 
int <- data.table(a = sample(1:26, 1e5, TRUE))    # int 

mbs <- function(...) { 
    ns <- sapply(match.call(expand.dots=TRUE)[-1L], deparse) 
    vals <- mget(ns, .GlobalEnv) 
    cat('Sizes:\n', 
     paste('\t', ns, ':', round(sapply(vals, object.size)/1024/1024, 3), 'MB\n')) 
} 

## Get approximate sizes? 
mbs(chars, string, fact, int) 
# Sizes: 
# chars : 0.765 MB 
# string : 0.766 MB 
# fact : 0.384 MB 
# int : 0.382 MB 
+2

Altri che leggono questa domanda potrebbero essere interessati al comportamento generale di R; secondo Hadley Wickham, "R ha un pool di stringhe globale: ciò significa che ogni stringa univoca viene memorizzata solo in un posto, e quindi i vettori di caratteri occupano meno memoria di quanto ci si potrebbe aspettare". Vedi http://adv-r.had.co.nz/memory.html. – Aaron

risposta

21

Si può essere ricordando data.table FAQ 2.17 che contiene:

stringsAsFactors è di default TRUE in data.frame ma FALSE in data.table, per l'efficienza. Poiché una cache di stringhe globale è stata aggiunta a R, gli elementi di caratteri sono un puntatore alla singola stringa memorizzata nella cache e non vi è più un vantaggio in termini di prestazioni di conversione in fattore.

(Quella parte è stato aggiunto alla FAQ in v1.8.2 nel luglio 2012.)

Utilizzando carattere piuttosto che fattore aiuta molto in compiti come impilare (rbindlist). Poiché un c() di due vettori di caratteri è solo la concatenazione mentre un c() di colonne di due fattori deve attraversare e unire i due livelli di fattore che è più difficile da codificare e richiede più tempo per essere eseguito.

Quello che hai notato è una differenza nel consumo di RAM su macchine a 64 bit. I fattori vengono memorizzati come una ricerca vettoriale integer degli elementi nei livelli. Il tipo integer è a 32 bit, anche su piattaforme a 64 bit. Ma i puntatori (che cos'è un vettore character) sono a 64 bit su macchine a 64 bit. Quindi una colonna di caratteri utilizzerà il doppio di RAM rispetto a una colonna di fattore su una macchina a 64 bit. Nessuna differenza su 32 bit. Tuttavia, di solito questo costo sarà superato dalle istruzioni più semplici e veloci possibili su un vettore di caratteri. [A parte: poiché i fattori sono integer, non possono contenere più di 2 miliardi di stringhe univoche. Le colonne character non hanno questa limitazione.]

Dipende da ciò che si sta facendo, ma le operazioni sono state ottimizzate per character in data.table e quindi questo è quello che consigliamo. Fondamentalmente si salva un salto (ai livelli) e possiamo confrontare due colonne di caratteri in diverse tabelle semplicemente confrontando i valori del puntatore senza saltare affatto, anche nella cache globale.

Dipende anche dalla cardinalità della colonna. Supponiamo che la colonna contenga 1 milione di righe e contenga 1 milione di stringhe univoche. Memorizzandolo come fattore sarà necessario un vettore di 1 milione di caratteri per i livelli più un vettore intero di 1 milione che punta agli elementi del livello. Questo è (4 + 8) * 1e6 byte. D'altro canto, un vettore di caratteri non avrà bisogno dei livelli ed è solo 8 * 1e6 byte. In entrambi i casi, la cache globale memorizza le 1 milione di stringhe univoche allo stesso modo, in modo che ciò avvenga comunque. In questo caso, la colonna di caratteri utilizzerà meno RAM che se fosse un fattore. Attento a controllare che lo strumento di memoria utilizzato per calcolare l'utilizzo della RAM stia calcolando questo in modo appropriato.

+3

@jenesaisquoi https://cran.r-project.org/doc/manuals/r-devel/R-ints.html#The-CHARSXP-cache quindi cerca mkChar() nell'origine. –

Problemi correlati