Come caricare i dati rapidamente in R?

Ho alcuni script R, in cui devo caricare diversi dataframe in R il più rapidamente ansible. Questo è abbastanza importante in quanto la lettura dei dati è la parte più lenta della procedura. Ad esempio: tracciamento da diversi dataframes. Ottengo il formato di dati in sav (SPSS), ma potrei trasformarlo in qualsiasi formato come suggerito. L’unione dei dataframes non è un’opzione, purtroppo.

Quale potrebbe essere il modo più veloce per caricare i dati? Stavo pensando a quanto segue:

  • trasforma da sav a object R binario ( Rdata ) per la prima volta, e in seguito lo carica sempre, poiché sembra molto più veloce di read.spss .
  • trasformare da sav in file CSV e leggere dati da quelli con determinati parametri discussi in questo argomento,
  • o vale la pena impostare un backend MySQL su localhost e caricare i dati da questo? Potrebbe essere più veloce? In tal caso, posso anche salvare eventuali valori attr personalizzazione delle variabili (ad esempio, variabili.labels dai file importati da Spss)? O questo dovrebbe essere fatto in una tabella separata?

Qualsiasi altro pensiero è benvenuto. Grazie per ogni suggerimento in anticipo!


Ho fatto un piccolo esperimento di seguito in base alle risposte che hai fornito, e ho anche aggiunto (24/01/2011) una soluzione abbastanza “hacker” ma veramente veloce caricando solo poche variabili / colonne da uno speciale file binario. Quest’ultimo sembra essere il metodo più veloce che io possa immaginare ora, ecco perché ho inventato (05/03/2011: versione 0.3) un piccolo pacchetto chiamato salva per gestire questa funzionalità. Il pacchetto è sotto lo sviluppo “pesante”, qualsiasi raccomandazione è benvenuta!

Pubblicherò presto una vignetta con risultati di benchmark precisi con l’aiuto del pacchetto microbenchmark .

Dipende da cosa si vuole fare e da come si elaborano ulteriormente i dati. In ogni caso, il caricamento da un object R binario sarà sempre più veloce, sempre che sia sempre necessario lo stesso set di dati. La velocità limite qui è la velocità del tuo disco fisso, non R. La forma binaria è la rappresentazione interna del dataframe nello spazio di lavoro, quindi non è più necessaria alcuna trasformazione.

Qualsiasi tipo di file di testo è una storia diversa, poiché includi invariabilmente un sovraccarico: ogni volta che leggi nel file di testo, i dati devono essere trasformati nell’object R binario. Mi dimenticherei di loro. Sono utili solo per il porting di set di dati da un’applicazione all’altra.

L’impostazione di un backend MySQL è molto utile se hai bisogno di parti diverse dei dati o di sottoinsiemi diversi in combinazioni diverse. Soprattutto quando si lavora con enormi set di dati, il fatto che non si debba caricare l’intero set di dati prima di poter iniziare a selezionare le righe / colonne, può farti guadagnare un po ‘di tempo. Ma questo funziona solo con enormi set di dati, poiché leggere un file binario è un po ‘più veloce della ricerca di un database.

Se i dati non sono troppo grandi, puoi salvare diversi dataframes in un unico file RData, dandoti l’opportunità di ottimizzare un po ‘di più le cose. Spesso ho un set di dataframe in un elenco o in un ambiente separato (vedi anche ?environment per alcuni semplici esempi). Ciò consente soluzioni lapply / eapply per elaborare più dataframe contemporaneamente.

Grazie a tutti per i suggerimenti e le risposte, ho fatto un riassunto e un esperimento basato su questo.

Vedi un piccolo test con un database pubblico ( ESS 2008 in Ungheria ) di seguito. Il database ha 1508 casi e 508 variabili, quindi potrebbe essere un dato di medie dimensioni. Questo potrebbe essere un buon esempio per fare il test (per me), ma naturalmente esigenze speciali richiederebbero un esperimento con dati adeguati.

Lettura dei dati dal file di salvataggio SPSS senza alcuna modifica:

 > system.time(data <- read.spss('ESS_HUN_4.sav')) user system elapsed 2.214 0.030 2.376 

Caricamento con un object binario convertito:

 > save('data',file='ESS_HUN_4.Rdata') > system.time(data.Rdata <- load('ESS_HUN_4.Rdata')) user system elapsed 0.28 0.00 0.28 

Cercando con CSV :

 > write.table(data, file="ESS_HUN_4.csv") > system.time(data.csv <- read.csv('ESS_HUN_4.csv')) user system elapsed 1.730 0.010 1.824 

Cercando con caricamento csv "fine-tuned":

 > system.time(data.csv <- read.table('ESS_HUN_4.csv', comment.char="", stringsAsFactors=FALSE, sep=",")) user system elapsed 1.296 0.014 1.362 

Anche con il pacchetto sqldf , che sembra caricare molto più velocemente i file csv:

 > library(sqldf) > f <- file("ESS_HUN_4.csv") > system.time(bigdf <- sqldf("select * from f", dbname = tempfile(), file.format = list(header = T, row.names = F, sep="\t"))) user system elapsed 0.939 0.106 1.071 

E anche il caricamento dei dati da un database MySQL in esecuzione su localhost:

 > library(RMySQL) > con <- dbConnect(MySQL(), user='root', dbname='test', host='localhost', password='') > dbWriteTable(con, "data", as.data.frame(data), overwrite = TRUE) > system.time(data <- dbReadTable(con, 'data')) user system elapsed 0.583 0.026 1.055 > query <-('SELECT * FROM data') > system.time(data.sql <- dbGetQuery(con, query)) user system elapsed 0.270 0.020 0.473 

Qui, penso che dovremmo aggiungere i due system.time riportati, poiché anche la connessione ai dati conta nel nostro caso. Per favore commenta, se ho frainteso qualcosa.

Ma vediamo se interrogare solo alcune variabili, come ad es. durante la stampa non abbiamo bisogno di tutti i dataframe nella maggior parte dei casi, e l'interrogazione di due sole variabili è sufficiente per creare una bella trama di essi:

 > query <-('SELECT c1, c19 FROM data') > system.time(data.sql <- dbGetQuery(con, query)) user system elapsed 0.030 0.000 0.112 

Che sembra davvero fantastico! Ovviamente subito dopo aver caricato la tabella con dbReadTable

Riepilogo: niente da battere leggendo l'intero dato dal file binario, ma leggere solo alcune colonne (o altri dati filtrati) dalla stessa tabella di database potrebbe essere anche ponderato in alcuni casi speciali.

Ambiente di test: laptop HP 6715b (AMD X2 2Ghz, 4 Gb DDR2) con un SSD di fascia bassa.


AGGIORNAMENTO (24/01/2011) : Ho aggiunto un modo piuttosto logico, ma abbastanza "creativo" di caricare solo poche colonne di un object binario - che sembra molto più veloce di qualsiasi metodo esaminato sopra.

Attenzione: il codice sembrerà davvero pessimo, ma comunque molto efficace 🙂

Per prima cosa, salverò tutte le colonne di un data.frame in diversi oggetti binari tramite il seguente ciclo:

 attach(data) for (i in 1:length(data)) { save(list=names(data)[i],file=paste('ESS_HUN_4-', names(data)[i], '.Rdata', sep='')) } detach(data) 

E poi carico due colonne di dati:

 > system.time(load('ESS_HUN_4-c19.Rdata')) + > system.time(load('ESS_HUN_4-c1.Rdata')) + > system.time(data.c1_c19 <- cbind(c1, c19)) user system elapsed 0.003 0.000 0.002 

Che sembra un metodo "superveloce"! 🙂 Nota: è stato caricato 100 volte più veloce del metodo più veloce (caricamento dell'intero object binario) sopra.

Ho creato un pacchetto molto piccolo (chiamato: salva ), guardo in github per ulteriori dettagli se interessati.


AGGIORNAMENTO (06/03/2011) : una nuova versione del mio piccolo pacchetto ( salva ) è stata caricata su CRAN, in cui è ansible salvare e caricare le variabili ancora più velocemente - se solo l'utente ha bisogno solo di un sottoinsieme delle variabili disponibili in una cornice dati o un elenco. Guarda la vignetta nelle fonti del pacchetto per i dettagli o quella sulla mia homepage , e lascia che ti presenti anche un bel boxplot di alcuni benchmark fatti:

Confronto di diversi meccanismi di caricamento di dati / frame per velocità

Questo boxplot mostra il vantaggio dell'utilizzo del pacchetto saves per caricare solo un sottoinsieme di variabili contro load e read.table o read.csv da base, read.spss da read.spss foreign o sqldf o RMySQL .

Se è ansible, i dati vengono trasformati in un csv o in un altro formato “semplice” per rendere la lettura il più veloce ansible (vedere la risposta di Joris). csv file csv in massa con la funzione apply , qualcosa sulla falsariga di:

 list.of.files <- as.list(list.files("your dir")) lapply(list.of.files, FUN = function(x) { my.object <- read.table(...) # or some other function, like read.spss }) 

Sono abbastanza soddisfatto di RMySQL. Non sono sicuro di avere la tua domanda nel modo giusto, ma le etichette non dovrebbero essere un problema. Esistono diverse funzioni di convenienza che utilizzano solo la tabella SQL e i nomi delle righe predefiniti, ma ovviamente è ansible utilizzare alcune istruzioni SQL.

Direi che (a parte grandi dataset che giustificano il trambusto) uno dei motivi principali per usare RMySQL è avere familiarità con la syntax SQL piuttosto che con le funzioni di giocoleria dei dati di R. Personalmente preferisco GROUP BY su aggregato. Si noti che l’utilizzo di stored procedure dall’interno di R non funziona particolarmente bene.

In conclusione … la configurazione di un localhost MySQL non è troppo difficile – provatelo! Non posso dire esattamente la velocità, ma ho la sensazione che ci sia una possibilità che sia più veloce. Tuttavia, cercherò di tornare qui.

EDIT: ecco il test … e il vincitore è: spacedman

 # SQL connection source("lib/connect.R") dbQuery <- "SELECT * FROM mytable" mydata <- dbGetQuery(con,dbQuery) system.time(dbGetQuery(con,dbQuery)) # returns #user system elapsed # 0.999 0.213 1.715 save.image(file="speedtest.Rdata") system.time(load("speedtest.Rdata")) #user system elapsed #0.348 0.006 0.358 

La dimensione del file era solo di circa 1 MB qui. MacBook Pro 4 GB Ram 2.4 GHZ Intel Core Duo, Mac OSX 10.6.4, MySQL 5.0.41 Non l'ho mai provato, perché lavoro di solito con un set di dati più grande e il caricamento non è il problema, piuttosto l'elaborazione ... se ci sono problemi di tempo affatto. +1 per la Q!