2011-11-04 6 views
10

Sto cercando di capire come utilizzare il pacchetto mmap per accedere a file csv di grandi dimensioni. Più precisamente, vorreimmap e file CSV

  1. Creare un oggetto mmap da un file csv con mmap.csv();
  2. Salvare il file creato da mmap.csv() contenente i dati in formato binario;
  3. Essere in grado di "mappare i dati binari in R" utilizzando la funzione mmap().

Il raggiungimento di 1. e 2. è facile: basta usare mmap.cv() e salvare l'tempfile() che contiene i dati binari, o modificare mmap.cv() per accettare un parametro aggiuntivo come file di output (e modificare la linea tmpstruct <- tempfile() di conseguenza). Ciò di cui ho problemi è 3. In particolare, ho bisogno di costruire una C-struct per i record nei dati binari dall'oggetto mmap. Ecco un semplice esempio riproducibile:

# create mmap object with its file 
library(mmap) 
data(cars) 

m <- as.mmap(cars, file="cars.Rmap") 
colnames(m) <- colnames(cars) 
str(m) 
munmap(m) 

Le informazioni da str() può essere utilizzato per costruire la C-struct record.struct che permette la mappatura del file binario cars.Rmap tramite la funzione mmap.

> str(m) 
<mmap:temp.Rmap> (struct) struct [1:50, 1:2] 4 ... 
    data   :<externalptr> 
    bytes  : num 400 
    filedesc  : Named int 27 
- attr(*, "names")= chr "temp.Rmap" 
    storage.mode :List of 2 
$ speed:Classes 'Ctype', 'int' atomic (0) 
    .. ..- attr(*, "bytes")= int 4 
    .. ..- attr(*, "signed")= int 1 
$ dist :Classes 'Ctype', 'int' atomic (0) 
    .. ..- attr(*, "bytes")= int 4 
    .. ..- attr(*, "signed")= int 1 
- attr(*, "bytes")= int 8 
- attr(*, "offset")= int [1:2] 0 4 
- attr(*, "signed")= logi NA 
- attr(*, "class")= chr [1:2] "Ctype" "struct" 
    pagesize  : num 4096 
    dim   :NULL 

In questo caso, abbiamo bisogno di due integer a 4 byte:

# load from disk 
record.struct <- struct(speed = integer(), # int32(), 4 byte int 
         dist = integer() # int32(), 4 byte int 
         ) 
m <- mmap("temp.Rmap", mode=record.struct) 

inferire il diritto C-struct può essere molto poco pratico per i file CSV "largo" (vale a dire i file con decine o centinaia di colonne). Ecco la mia domanda: Come si può costruire direttamente dall'oggetto mmap m?

+0

Non so nulla di mmap, ma volevo solo assicurarmi che tu sappia che c'è una vignetta: http://cran.r-project.org/web/packages/mmap/vignettes/mmap.pdf –

+0

@XuWang: grazie, sono a conoscenza della vignetta. Questo è quello che mi ha fatto passare attraverso 1 e 2. – Ryogi

risposta

8

Un esempio più o meno completa di ciò che si sta chiedendo - con mmap e mmap.csv

data(mtcars) 
tmp <- tempfile() 
write.csv(mtcars, tmp) 
m <- mmap.csv(tmp) # mmap in the csv 
head(m) 
        X mpg cyl disp hp drat wt qsec vs am gear carb 
1 Mazda RX4   21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 
2 Mazda RX4 Wag  21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 
3 Datsun 710   22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 
4 Hornet 4 Drive  21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 
5 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 
6 Valiant    18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 


st <- m$storage.mode 

## since m is already mmap'd as a binary, we'll use that here - but you'd store this 
m1 <- mmap(attr(m$filedesc, "names"), mode=st, extractFUN=as.data.frame) 

head(m1) 
        X mpg cyl disp hp drat wt qsec vs am gear carb 
1 Mazda RX4   21.0 6 160 110 3.90 2.620 16.46 0 1 4 4 
2 Mazda RX4 Wag  21.0 6 160 110 3.90 2.875 17.02 0 1 4 4 
3 Datsun 710   22.8 4 108 93 3.85 2.320 18.61 1 1 4 1 
4 Hornet 4 Drive  21.4 6 258 110 3.08 3.215 19.44 1 0 3 1 
5 Hornet Sportabout 18.7 8 360 175 3.15 3.440 17.02 0 0 3 2 
6 Valiant    18.1 6 225 105 2.76 3.460 20.22 1 0 3 1 

come una risposta precedente menziona, m $ storage.mode è la modalità si ha bisogno.

Si potrebbe fare un ulteriore passo avanti e memorizzare la modalità in un file utilizzando alcune convenzioni di denominazione del proprio progetto. È anche possibile creare un oggetto binario personalizzato utilizzando la coppia len e disattivando in mmap.

+0

Grazie Jeff. Non mi rendevo conto che 'm $ storage.mode' poteva essere usato direttamente – Ryogi

+0

Questo è molto più semplice. C'è qualche possibilità che tu sia lo stesso Jeff Ryan che ha creato il pacchetto 'mmap'? :) – Iterator

+2

Rispondendo a me stesso: sì - e ci sono alcuni bei articoli su [il tuo sito] (http://www.lemnica.com/esotericR/). La maggior parte dei tuoi pacchetti se non tutti sono sulla mia lista per provare. Sono contento di vedere che sei su SO. :) – Iterator

3

Questo dovrebbe funzionare:

varClasses <- rapply(m$storage.mode, typeof) 

Ecco cosa ottengo:

> rapply(m$storage.mode, typeof) 
    speed  dist 
"double" "double" 

(Questo è dovuto al cars essere memorizzati come doppie nella mia versione di R. risultati delle partite tua quando il tipo di è cambiato in interi -. vedi Update 1, di seguito)

Usando questo per creare l'oggetto struct è semplicemente una questione di sostituire questi tipi con i tipi C appropriati (ad es. cambiare int a integer), che può essere fatto tramite una ricerca di lista, e quindi è possibile utilizzare paste per creare l'elenco appropriato di argomenti.


Ecco cosa m assomiglia per me, utilizzando gli stessi comandi come avete dato:

> str(m) 
<mmap:/tmp/Rtmpz...> (struct) struct [1:50, 1:2] 4 ... 
    data   :<externalptr> 
    bytes  : num 800 
    filedesc  : Named int 3 
- attr(*, "names")= chr "/tmp/RtmpzGwIDT/file77aa9d47" 
    storage.mode :List of 2 
$ speed:Classes 'Ctype', 'double' atomic (0) 
    .. ..- attr(*, "bytes")= int 8 
    .. ..- attr(*, "signed")= int 1 
$ dist :Classes 'Ctype', 'double' atomic (0) 
    .. ..- attr(*, "bytes")= int 8 
    .. ..- attr(*, "signed")= int 1 
- attr(*, "bytes")= int 16 
- attr(*, "offset")= int [1:2] 0 8 
- attr(*, "signed")= logi NA 
- attr(*, "class")= chr [1:2] "Ctype" "struct" 
    pagesize  : num 4096 
    dim   :NULL 

Update 1: Quando mi sono convertito esplicitamente cars a numeri interi, e ha assicurato l'oggetto era un data frame (ovvero cars2 <- as.data.frame(apply(cars, 2, as.integer)); colnames(cars2) = colnames(cars)), tutto funziona e lo rapply produce "integer", come previsto.

Aggiornamento 2: Ecco hackerare a creare gli argomenti interni da passare a struct():

oTypes = rapply(m$storage.mode, typeof) 
lNames = names(oTypes) 
lTypes = as.character(oTypes) 
lTypes = paste(lTypes,'()', sep = "") 
lArgs = paste(lNames, lTypes, sep = "=", collapse = ",") 

Si tratta di un'approssimazione, perché ho il sospetto che lTypes deve essere convertito da R a tipi C.

+0

Grazie Iterator. Ho accettato con la risposta di Jeff a causa dell'uso diretto di 'storage.mode'. – Ryogi

+0

Grazie per aver fatto una bella domanda. Ero sicuro che doveva esserci un modo migliore di discutere i tipi come ho fatto io. :) Comunque, sembrava divertente provare e vedere dove si potevano ottenere i tipi di dati. – Iterator

4

sto dando un'altra risposta, perché la prima risposta è per la questione primaria (? Come si può costruire record.struct direttamente dall'oggetto mmap m), tuttavia, penso che sia anche possibile indirizzare il predicato: "Dedurre la right C-struct può essere molto poco pratico per file "wide" csv (cioè file con decine o centinaia di colonne). " La mia motivazione è di dissipare l'idea che è difficile ottenere informazioni sul tipo per i file CSV. :)

Supponendo che i dati è regolare (cioè colonna per atomica, che deve essere se stesse per arrivare la memoria mappata), allora si potrebbe semplicemente fare questo:

tmpDF <- read.csv(myFile, nrow = 10) 
myClasses <- rapply(tmpDF, typeof) 

Così, si leggere solo una piccola quantità di informazioni e lasciare che R determini le classi per te. Potrebbe essere necessario risolvere il problema stringsAsFactors, ad esempio tramite read.csv(..., stringsAsFactors = FALSE).