Ritaglio di un enorme file csv (3,5 GB) da leggere in R

Quindi ho un file di dati (separato da punto e virgola) che ha un sacco di dettagli e righe incomplete (che portano Access e SQL a soffocare). È un set di dati a livello di contea suddiviso in segmenti, sottosegmenti e sotto-segmenti secondari (per un totale di ~ 200 fattori) per 40 anni. In breve, è enorme e non si adatta alla memoria se provo a leggerlo semplicemente.

Quindi la mia domanda è questa, dato che voglio tutte le contee, ma solo un anno (e solo il livello più alto del segmento … che porta a circa 100.000 righe alla fine), quale sarebbe il modo migliore per ottenere questo rollup in R?

Attualmente sto cercando di ridurre gli anni irrilevanti con Python, aggirando il limite di file leggendo e operando su una riga alla volta, ma preferirei una soluzione solo R (pacchetti CRAN OK). C’è un modo simile di leggere un file alla volta in R?

Qualsiasi idea sarebbe molto apprezzata.

Aggiornare:

  • vincoli
    • Ha bisogno di usare la mia macchina, quindi nessuna istanza EC2
    • Come solo R ansible. La velocità e le risorse non sono preoccupazioni in questo caso … a condizione che la mia macchina non esploda …
    • Come puoi vedere di seguito, i dati contengono tipi misti, su cui ho bisogno di operare più tardi
  • Dati
    • I dati sono 3,5 GB, con circa 8,5 milioni di righe e 17 colonne
    • Un paio di migliaia di righe (~ 2k) sono malformate, con solo una colonna invece di 17
      • Questi sono del tutto irrilevanti e possono essere abbandonati
    • Ho solo bisogno di ~ 100.000 righe da questo file (vedi sotto)

Esempio di dati:

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ... Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ... Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ... NC [Malformsd row] [8.5 Mill rows] 

Voglio tagliare alcune colonne e scegliere due dei 40 anni disponibili (2009-2010 dal 1980-2020), in modo che i dati possano rientrare in R:

 County; State; Year; Quarter; Segment; GDP; ... Ada County;NC;2009;4;FIRE;80.1; ... Ada County;NC;2010;1;FIRE;82.5; ... [~200,000 rows] 

risultati:

Dopo aver armeggiato con tutti i suggerimenti fatti, ho deciso che readLines, suggerito da JD e Marek, avrebbe funzionato meglio. Ho dato a Marek l’assegno perché ha dato un esempio di implementazione.

Ho riprodotto una versione leggermente adattata dell’implementazione di Marek per la mia risposta finale qui, usando strsplit e cat per mantenere solo le colonne che voglio.

Va anche notato che questo è MOLTO meno efficiente di Python … come in Python, fa il chomp attraverso il file da 3,5 GB in 5 minuti mentre R impiega circa 60 … ma se tutto ciò che hai è R allora questo è il ticket.

 ## Open a connection separately to hold the cursor position file.in <- file('bad_data.txt', 'rt') file.out <- file('chopped_data.txt', 'wt') line <- readLines(file.in, n=1) line.split <- strsplit(line, ';') # Stitching together only the columns we want cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE) ## Use a loop to read in the rest of the lines line <- readLines(file.in, n=1) while (length(line)) { line.split  1) { if (line.split[[1]][3] == '2009') { cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE) } } line<- readLines(file.in, n=1) } close(file.in) close(file.out) 

Fallimenti per approccio:

  • sqldf
    • Questo è sicuramente quello che userò per questo tipo di problema in futuro se i dati sono ben formati. Tuttavia, se non lo è, allora SQLite soffoca.
  • Riduci mappa
    • Ad essere onesti, i doc mi hanno intimidito su questo un po ‘, quindi non sono riuscito a provarlo. Sembrava che richiedesse anche l’object in memoria, il che avrebbe vanificato il punto se fosse il caso.
  • bigmemory
    • Questo approccio è chiaramente collegato ai dati, ma può gestire solo un tipo alla volta. Di conseguenza, tutti i miei vettori di personaggi sono caduti quando sono stati messi in un grande tavolo. Se devo progettare grandi set di dati per il futuro, prenderei in considerazione solo l’uso dei numeri solo per mantenere viva questa opzione.
  • scansione
    • La scansione sembrava avere problemi di tipo simile alla memoria grande, ma con tutti i meccanismi di readLines. In breve, questa volta non è andato bene il conto.

Il mio tentativo con readLines . Questo pezzo di codice crea csv con anni selezionati.

 file_in < - file("in.csv","r") file_out <- file("out.csv","a") x <- readLines(file_in, n=1) writeLines(x, file_out) # copy headers B <- 300000 # depends how large is one pack while(length(x)) { ind <- grep("^[^;]*;[^;]*; 20(09|10)", x) if (length(ind)) writeLines(x[ind], file_out) x <- readLines(file_in, n=B) } close(file_in) close(file_out) 

C’è un modo simile di leggere un file alla volta in R?

Sì. La funzione readChar () legge in un blocco di caratteri senza presupporre che siano terminati con null. Se si desidera leggere i dati in una riga alla volta, è ansible utilizzare readLines () . Se si legge un blocco o una linea, eseguire un’operazione, quindi scrivere i dati, è ansible evitare il problema di memoria. Anche se hai voglia di triggersre una grande istanza di memoria su EC2 di Amazon, puoi ottenere fino a 64 GB di RAM. Questo dovrebbe contenere il tuo file e molto spazio per manipolare i dati.

Se hai bisogno di più velocità, la raccomandazione di Shane di usare Map Reduce è molto buona. Tuttavia, se si sceglie di utilizzare una grande istanza di memoria su EC2, si dovrebbe considerare il pacchetto multicore per l’utilizzo di tutti i core su una macchina.

Se ti accorgi di voler leggere molti concerti di dati delimitati in R, dovresti almeno ricercare il pacchetto sqldf che ti permette di importare direttamente in sqldf da R e poi operare sui dati da R. Ho trovato sqldf come uno dei modi più veloci per importare concerti di dati in R, come menzionato in questa domanda precedente .

Non sono un esperto in questo, ma potresti pensare di provare MapReduce , il che significherebbe fondamentalmente prendere un approccio “divide et impera”. R ha diverse opzioni per questo, tra cui:

  1. mapReduce (pure R)
  2. RHIPE (che usa Hadoop ); vedere l’esempio 6.2.2 nella documentazione per un esempio di file di subsetting

In alternativa, R fornisce diversi pacchetti per gestire grandi quantità di dati che vanno fuori dalla memoria (sul disco). Probabilmente si potrebbe caricare l’intero set di dati in un object di bigmemory e fare la riduzione completamente all’interno di R. Vedere http://www.bigmemory.org/ per un set di strumenti per gestire questo.

Il pacchetto ff è un modo trasparente per gestire file enormi.

Potresti vedere il sito web del pacchetto e / o una presentazione a riguardo.

Spero che aiuti

È ansible importare dati nel database SQLite e quindi utilizzare RSQLite per selezionare sottoinsiemi.

C’è un nuovo pacchetto chiamato colbycol che ti permette di leggere solo le variabili che vuoi da enormi file di testo:

http://colbycol.r-forge.r-project.org/

Passa qualsiasi argomento lungo read.table, quindi la combinazione dovrebbe permetterti di creare un sottoinsieme piuttosto stretto.

Che dire dell’uso di readr e della famiglia di read_*_chunked ?

Quindi nel tuo caso:

testfile.csv

 County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP Ada County;NC;2009;4;FIRE;Financial;Banks;80.1 Ada County;NC;2010;1;FIRE;Financial;Banks;82.5 lol Ada County;NC;2013;1;FIRE;Financial;Banks;82.5 

Codice reale

 require(readr) f < - function(x, pos) subset(x, Year %in% c(2009, 2010)) read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1) 

Questo vale per ogni blocco, ricordando i nomi dei nomi e combinando i risultati filtrati alla fine. Vedi ?callback che è la fonte di questo esempio.

Questo risulta in:

 # A tibble: 2 × 8 County State Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment` GDP *         1 Ada County NC 2009 4 FIRE Financial Banks 801 2 Ada County NC 2010 1 FIRE Financial Banks 825 

Puoi persino aumentare chunk_size ma in questo esempio ci sono solo 4 linee.

Hai consacrato bigmemory ? Dai un’occhiata a questo e questo .

Forse è ansible migrare a MySQL o PostgreSQL per evitare le limitazioni di MS Access.

È abbastanza facile colbind R a questi sistemi con un connettore di database basato su DBI (disponibile su CRAN).

scan () ha sia un argomento nlines che un argomento skip. C’è qualche ragione per cui puoi semplicemente usarlo per leggere un pezzo di righe in un momento, controllando la data per vedere se è appropriato? Se il file di input è ordinato in base alla data, è ansible memorizzare un indice che indichi quali sono gli skip e le nlines che potrebbero accelerare il processo in futuro.

Al giorno d’oggi, 3,5 GB non è proprio così grande, posso accedere a una macchina con RAM da 244 GB (r3.8xlarge) sul cloud Amazon per $ 2,80 / ora. Quante ore ci vorrà per capire come risolvere il problema utilizzando soluzioni di tipo big-data? Quanto vale il tuo tempo? Sì, ci vorranno un’ora o due per capire come usare AWS – ma puoi imparare le basi su un tier gratuito, caricare i dati e leggere le prime 10k lines in R per controllare che funzioni e quindi puoi accendere un grande istanza di memoria come r3.8xlarge e leggi tutto! Solo il mio 2c.

Ora, 2017, suggerirei di andare per spark and sparkR.

  • la syntax può essere scritta in un modo semplice piuttosto dplyr-simile

  • si adatta abbastanza bene alla memoria piccola (piccola nel senso del 2017)

Tuttavia, potrebbe essere un’esperienza intimidatoria per iniziare …

Vorrei andare per un DB e quindi fare alcune domande per estrarre i campioni necessari tramite DBI

Evita di importare un file CSV da 3,5 GB in SQLite. O almeno controlla che il tuo db ENORME si adatti ai limiti di SQLite, http://www.sqlite.org/limits.html

È un dannato grande DB che hai. Vorrei andare su MySQL se hai bisogno di velocità. Ma sii preparato ad aspettare un sacco di ore affinché l’importazione finisca. A meno che tu non abbia un hardware non convenzionale o stai scrivendo dal futuro …

Amazon EC2 potrebbe essere una buona soluzione anche per istanziare un server con R e MySQL.

i miei due umili penny vale la pena.