2013-10-06 5 views
5

Sono occupato nella pulizia dei dati. Ho una funzione che identifica le righe danneggiate in un file di input di grandi dimensioni (troppo grande per essere letto in una volta sola, data la dimensione della ram) e restituisce i numeri di riga delle righe danneggiate come un vettore badRows. Questa funzione sembra funzionare.Come posso leggere le righe selezionate da un file di grandi dimensioni utilizzando il comando R "readLines" e scriverle su un frame di dati?

Ora sto provando a leggere solo le righe danneggiate in un frame di dati, finora senza successo.

Il mio approccio attuale è quello di utilizzare read.table su una connessione aperta al mio file, utilizzando un vettore del numero di righe da saltare tra ogni riga che viene letta. Questo numero è zero per le righe danneggiate consecutive.

ho calcolare skipVec come:

(badRowNumbers - c(0, badRowNumbers[1:(length(badRowNumbers-1]))-1 

Ma per il momento io sono solo distribuendo la mia funzione un skipVec vettore di tutti gli zeri.

Se la mia logica è corretta, questo dovrebbe restituire tutte le righe. Non è così. Invece ottengo un errore:

"Error in read.table(con, skip = pass, nrow = 1, header = TRUE, sep = "") : no lines available in input"

La mia funzione attuale è vagamente basato su una funzione da Miron Kursa ("MBq"), che ho trovato here.

La mia domanda è in qualche modo duplicativa di quella, ma presumo che la sua funzione funzioni, quindi l'ho interrotta in qualche modo. Sto ancora cercando di capire la differenza tra l'apertura di un file e l'apertura di una connessione a un file, e ho il sospetto che il problema ci sia da qualche parte o nel mio utilizzo di lapply.

Sto utilizzando R 3.0.1 sotto RStudio 0.97.551 su una vecchia macchina Windows XP SP3 con 3gig di ram. Stone Age, lo so.

Ecco il codice che genera il messaggio di errore precedente:

# Make a small small test data frame, write it to a file, and read it back in 
# a row at a time. 
testThis.DF <- data.frame(nnn=c(2,3,5), fff=c("aa", "bb", "cc")) 
testThis.DF 

# This function will work only if the number of bad rows is not too big for memory 
write.table(testThis.DF, "testThis.DF") 
con<-file("testThis.DF") 
open(con) 
skipVec <- c(0,0,0) 
badRows.DF <- lapply(skipVec, FUN=function(pass){ 
    read.table(con, skip=pass, nrow=1, header=TRUE, sep="") }) 
close(con) 

L'errore si verifica prima che il comando di chiusura. Se strappo il comando readLines dal lapply e dalla funzione e lo inserisco da solo, continuo a ricevere lo stesso errore.

risposta

5

Se invece di correre read.table attraverso lapply basta eseguire le prime iterazioni manualmente, si vedrà che cosa sta succedendo:

> read.table(con, skip=0, nrow=1, header=TRUE, sep="") 
    nnn fff 
1 2 aa 
> read.table(con, skip=0, nrow=1, header=TRUE, sep="") 
    X2 X3 bb 
1 3 5 cc 

Perché header = TRUE non è una linea che viene letto ad ogni iterazione, ma due , quindi alla fine si esegue fuori delle linee più velocemente di quanto si pensi, qui la terza iterazione:

> read.table(con, skip=0, nrow=1, header=TRUE, sep="") 
Error in read.table(con, skip = 0, nrow = 1, header = TRUE, sep = "") : 
    no lines available in input 

Ora questo potrebbe ancora non essere un modo molto efficace di risolvere il tuo problema, ma questo è come si in grado di correggere il codice corrente:

write.table(testThis.DF, "testThis.DF") 
con <- file("testThis.DF") 
open(con) 
header <- scan(con, what = character(), nlines = 1, quiet = TRUE) 
skipVec <- c(0,1,0) 
badRows <- lapply(skipVec, function(pass){ 
    line <- read.table(con, nrow = 1, header = FALSE, sep = "", 
        row.names = 1) 
    if (pass) NULL else line 
    }) 
badRows.DF <- setNames(do.call(rbind, badRows), header) 
close(con) 

Alcuni indizi verso velocità più elevate:

  1. uso scan invece di read.table. Leggere i dati come character e solo alla fine, dopo aver inserito i dati in una matrice di caratteri o data.frame, applicare type.convert a ciascuna colonna.
  2. Invece di eseguire il ciclo su skipVec, eseguire il ciclo su rle se è molto più breve. Quindi sarai in grado di leggere o saltare pezzi di linee alla volta.
+0

Caro @ flodel- Questo codice non funziona, ma penso che questo sia solo un errore di battitura, perché funziona per skipVec = 0,0,0 invece di 0,1,0. Sebbene funzioni, sono imbarazzato nell'ammettere che mi manca una parte della logica. Per prima cosa, non so perché scannerizzi la prima riga e usi read.table per il resto. Ma la mia più grande confusione, forse perché non capisco come i file e le connessioni sono elaborati in modo diverso, è che non vedo perché skipVec = 0,0,0 non inizia alla prima riga. Inoltre, non so cosa sia un rle. Ho letto la funzione doc, ma non so che cos'è una corsa o una lunghezza della corsa, o come scoprirlo. – andrewH

+0

* Questo codice non funziona * non è molto utile per me a meno che tu non mi dica perché non funziona. Uscita imprevista, messaggio di errore? Nota che funziona con il tuo esempio, quindi forse il tuo file di dati reali ha un formato diverso da quello che ci stai costruendo in questo esempio. – flodel

+0

Una connessione è come un file che viene tenuto aperto, quindi è possibile accedervi più volte per la lettura o la scrittura. La connessione include un puntatore a dove sono stati letti o scritti i dati. Quando apri per la prima volta il file, l'handle del file punta all'inizio del file. Dopo aver usato 'read.table (con, skip = 0, nrow = 1, header = TRUE, sep =" ")' la prima volta vengono letti l'intestazione e la prima riga di dati del tuo file, quindi il puntatore sta puntando all'inizio della seconda riga di dati. – flodel

Problemi correlati