2012-05-15 11 views
7

Ho un file di grandi dimensioni che contiene molti dati e mi piacerebbe leggerlo in dataframe, ma ho trovato alcune righe non valide. Queste righe non valide causano la rottura di read.table. Provo il seguente metodo per saltare le righe non valide, ma sembra che le prestazioni siano pessime.Come saltare le righe non valide durante la lettura del frame di dati dal file in R?

counts<-count.fields(textConnection(lines),sep="\001") 
raw_data<-read.table(textConnection(lines[counts == 34]), sep="\001") 

C'è un modo migliore per raggiungere questo obiettivo? Grazie

+2

Cosa c'è di male nella tua definizione? –

+1

Qualche ragione per cui non stai usando 'read.table' direttamente? Ha un sacco di argomenti per selezionare e ignorare vari personaggi "cattivi". C'è anche un argomento per "riempire" le righe incomplete, se questo è il problema che stai riscontrando. –

risposta

18

utilizzando @ dati di esempio di PaulHiemstra:

read.table("test.csv", sep = ";", fill=TRUE) 

poi si prende cura di AN come si desidera.

+1

Ho aggiunto la tua risposta come opzione aggiuntiva ai benchmark –

+0

Lazy me-- questa è stata la mia risposta nel mio primo commento, ma l'hai scritta con dettagli molto migliori –

+0

@Carl, +1 per il tuo commento karma. – BenBarnes

5

Quello che potresti fare è scorrere le righe nel file e aggiungere solo le linee che hanno la lunghezza corretta.

ho definito il seguente file di test CSV:

1;2;3;4 
1;2;3;4 
1;2;3 
1;2;3;4 

Utilizzando read.table fallisce:

> read.table("test.csv", sep = ";") 
Error in scan(file, what, nmax, sep, dec, quote, skip, nlines, na.strings, :                  
    line 3 did not have 4 elements 

Ora un approccio iterativo:

require(plyr) 
no_lines = 4 
correct_length = 4 
file_con = file("test.csv", "r") 
result = ldply(1:no_lines, function(line) { 
    dum = strsplit(readLines(file_con, n = 1), split = ";")[[1]] 
    if(length(dum) == correct_length) { 
    return(dum) 
    } else { 
    cat(sprintf("Skipped line %s\n", line)) 
    return(NULL) 
    } 
    }) 
close(file_con) 

> result 
    V1 V2 V3 V4 
1 1 2 3 4 
2 1 2 3 4 
3 1 2 3 4 

Naturalmente questo è un esempio banale come il il file è veramente piccolo Cerchiamo di creare un esempio più stimolante per agire come un punto di riferimento.

# First file with invalid rows 
norow = 10e5 # number of rows 
no_lines = round(runif(norow, min = 3, max = 4)) 
no_lines[1] = correct_length 
file_content = ldply(no_lines, function(line) paste(1:line, collapse = ";")) 
writeLines(paste(file_content[[1]], sep = "\n"), "big_test.csv") 

# Same length with valid rows 
file_content = ldply(rep(4, norow), function(line) paste(1:line, collapse = ";")) 
writeLines(paste(file_content[[1]], sep = "\n"), "big_normal.csv") 

Ora, per il punto di riferimento

# Iterative approach 
system.time({file_con <- file("big_test.csv", "r") 
    result_test <- ldply(1:norow, function(line) { 
     dum = strsplit(readLines(file_con, n = 1), split = ";")[[1]] 
     if(length(dum) == correct_length) { 
     return(dum) 
     } else { 
     # Commenting this speeds up by 30% 
     #cat(sprintf("Skipped line %s\n", line)) 
     return(NULL) 
     } 
     }) 
    close(file_con)}) 
    user system elapsed 
20.559 0.047 20.775 

# Normal read.table 
system.time(result_normal <- read.table("big_normal.csv", sep = ";")) 
    user system elapsed 
    1.060 0.015 1.079 

# read.table with fill = TRUE 
system.time({result_fill <- read.table("big_test.csv", sep = ";", fill=TRUE) 
      na_rows <- complete.cases(result_fill) 
      result_fill <- result_fill[-na_rows,]}) 
    user system elapsed 
    1.161 0.033 1.203 

# Specifying which type the columns are (e.g. character or numeric) 
# using the colClasses argument. 
system.time({result_fill <- read.table("big_test.csv", sep = ";", fill=TRUE, 
             colClasses = rep("numeric", 4)) 
      na_rows <- complete.cases(result_fill) 
      result_fill <- result_fill[-na_rows,]}) 
    user system elapsed 
    0.933 0.064 1.001 

Così l'approccio iterativo è un po 'più lento, ma 20 secondi per 1 milione di righe potrebbe essere accettabile (anche se questo dipende dalla vostra definizione di accettabile). Soprattutto quando devi farlo solo una volta, e poi salvarlo usando save per il successivo recupero. La soluzione suggerita da @Paolo è quasi veloce come la normale chiamata a read.table. Le righe che contengono la quantità errata di colonne (ovvero NA) vengono eliminate utilizzando complete.cases. Specificando quali classi le colonne sono ulteriormente migliorano le prestazioni, e penso che questo effetto sarà più grande quando la quantità di colonne e righe aumenta.

Quindi, in conclusione, l'opzione migliore è utilizzare read.table con fill = TRUE, specificando le classi delle colonne. L'approccio iterativo che utilizza ldply è solo una buona opzione se desideri una maggiore flessibilità nella scelta di come leggere le righe, ad es. legge solo la riga se un determinato valore supera una soglia. Ma probabilmente questo potrebbe essere fatto più velocemente leggendo tutti i dati in R e poi creando un sottoinsieme. Solo quando i dati sono più grandi della RAM, posso immaginare che l'approccio iterativo abbia i suoi meriti.

Problemi correlati