2013-07-18 11 views
5

R sembra richiedere quattro byte di archiviazione per intero, anche per i più piccoli:risparmiare spazio di archiviazione per i piccoli numeri interi o fattori con alcuni livelli

> object.size(rep(1L, 10000)) 
40040 bytes 

E, per di più, anche per fattori:

> object.size(factor(rep(1L, 10000))) 
40456 bytes 

Penso che, soprattutto in quest'ultimo caso, questo potrebbe essere gestito molto meglio. C'è una soluzione che mi aiuti a ridurre i requisiti di archiviazione per questo caso a otto o anche due bit per riga? Forse una soluzione che usa il tipo raw internamente per la memorizzazione ma si comporta diversamente come un normale fattore. Il pacchetto bit offre questo per bit, ma non ho trovato nulla di simile per i fattori.

mio frame di dati con pochi milioni di righe è consumando gigabyte, e questo è un enorme spreco di memoria e tempo di esecuzione (!). La compressione ridurrà lo spazio su disco richiesto, ma nuovamente a spese del tempo di esecuzione.

correlati: codifiche

+1

I frame di dati non sono in grado di gestire dati di tale ampiezza. Hai bisogno di 'data.table'. –

+0

@HongOoi: In che modo 'data.table' mi aiuterà a risolvere il problema? – krlmlr

+0

@Arun: Supponiamo che per lo più si stia eseguendo il looping dei dati, in modo che 'data.table' non sia di grande aiuto. – krlmlr

risposta

6

Dal momento che si parla raw (e supponendo che si hanno meno di 256 livelli di fattore) - si potrebbe fare le operazioni di conversione dei prerequisiti se la memoria è il collo di bottiglia e il tempo di CPU non è. Per esempio:

f = factor(rep(1L, 1e5)) 
object.size(f) 
# 400456 bytes 

f.raw = as.raw(f) 
object.size(f.raw) 
#100040 bytes 

# to go back: 
identical(as.factor(as.integer(f.raw)), f) 
#[1] TRUE 

È inoltre possibile salvare i livelli di fattore separatamente e recuperarli se questo è qualcosa che ti interessa facendo, ma per quanto riguarda il raggruppamento e tutto ciò che va solo si può fare tutto con raw e mai tornare ai fattori (ad eccezione della presentazione).

Se si dispone di casi d'uso specifici in cui avete problemi con questo metodo, si prega di postare, altrimenti credo che questo dovrebbe funzionare bene.


Ecco un punto di partenza per il vostro byte.factor classe:

byte.factor = function(f) { 
    res = as.raw(f) 
    attr(res, "levels") <- levels(f) 
    attr(res, "class") <- "byte.factor" 
    res 
} 

as.factor.byte.factor = function(b) { 
    factor(attributes(b)$levels[as.integer(b)], attributes(b)$levels) 
} 

modo da poter fare le cose come:

f = factor(c('a','b'), letters) 
f 
#[1] a b 
#Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z 

b = byte.factor(f) 
b 
#[1] 01 02 
#attr(,"levels") 
# [1] "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" "l" "m" "n" "o" "p" "q" "r" "s" 
#[20] "t" "u" "v" "w" "x" "y" "z" 
#attr(,"class") 
#[1] "byte.factor" 

as.factor.byte.factor(b) 
#[1] a b 
#Levels: a b c d e f g h i j k l m n o p q r s t u v w x y z 

verificare come data.table sostituzioni rbind.data.frame se si vuole fare as.factor generici e aggiungi semplicemente le funzioni che desideri aggiungere. Dovrebbe essere tutto abbastanza semplice.

+0

Questo è un buon inizio. Quanto sarebbe difficile implementare un 'byte.factor 'di classe che si comporta come un' fattore' per tutti gli scopi pratici ma usa 'raw' come memoria? Di cosa avremmo bisogno per sovrascrivere/implementare? – krlmlr

+0

@krlmlr punto di partenza di base aggiunto – eddi

2

Un po 'fuori dagli schemi, ma l'esecuzione di lunghezza potrebbe essere appropriato per lunghi fattori di alcuni livelli, a condizione che gli elementi sono ordinati per in qualche misura; questo può essere supportato dal pacchetto IRanges in Bioconductor

rle = Rle(factor("A"), 1000000) 
df = DataFrame(rle=rle) 

e

> object.size(rle) 
1528 bytes 

DataFrame e Rle supporto tutte le operazioni standard, per esempio, sottoinsiemi, aggiunta di RLE di. Ovviamente il risparmio di dimensioni dipende in modo cruciale dal mantenimento dell'ordine ordinato.

+0

Questa è una bella opzione, simile a "bitwhich'. Grazie per averlo indicato, anche se sfortunatamente i miei dati non sembrano essere RLE-comprimibili ... – krlmlr

3

Un'altra soluzione utilizza ff. ff supporta i seguenti tipi vmodes/(vedi ?vmode):

‘boolean’ ‘as.boolean’ 1 bit logical without NA   
‘logical’ ‘as.logical’ 2 bit logical with NA    
‘quad’  ‘as.quad’  2 bit unsigned integer without NA 
‘nibble’  ‘as.nibble’  4 bit unsigned integer without NA 
‘byte’  ‘as.byte’  8 bit signed integer with NA  
‘ubyte’  ‘as.ubyte’  8 bit unsigned integer without NA 
‘short’  ‘as.short’  16 bit signed integer with NA  
‘ushort’  ‘as.ushort’  16 bit unsigned integer without NA 
‘integer’ ‘as.integer’ 32 bit signed integer with NA  
‘single’  ‘as.single’  32 bit float      
‘double’  ‘as.double’  64 bit float      
‘complex’ ‘as.complex’ 2x64 bit float      
‘raw’  ‘as.raw’  8 bit unsigned char     
‘character’ ‘as.character’ character 

Ad esempio:

library(ff) 
v <- ff(as.factor(sample(letters[1:4], 10000, replace=TRUE)), vmode="byte", 
    levels=letters[1:4]) 

Questo userà solo un byte per elemento. Un ulteriore vantaggio/svantaggio è che quando i dati diventano troppo grandi per essere archiviati in memoria, vengono automaticamente archiviati su disco (il che ovviamente influirà sulle prestazioni).

Tuttavia, qualunque sia la soluzione che si utilizza, probabilmente si avranno prestazioni ridotte. R utilizza internamente numeri interi per i fattori, quindi prima di chiamare qualsiasi metodo R i dati dovranno essere tradotti dalla memoria compatta agli interi di R, il che avrà un costo. A meno che non si utilizzino solo metodi specificamente scritti per il tipo di archiviazione compatta (questi dovranno probabilmente essere scritti in c/C++/...).

Problemi correlati