2012-05-15 14 views
7

Tengo un archivo grande que contiene muchos datos, y me gustaría leerlo en el marco de datos, pero encontré algunas filas no válidas. Estas filas no válidas hacen que read.table se rompa. Intento el siguiente método para saltear líneas inválidas, pero parece que el rendimiento es muy malo.Cómo omitir las filas no válidas al leer el marco de datos del archivo en R?

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

¿Hay alguna manera mejor de lograr esto? Gracias

+2

¿Qué es malo en tu definición? –

+1

¿Alguna razón por la que no está usando 'read.table' directamente? Tiene muchos argumentos para seleccionar e ignorar varios personajes "malos". También hay un argumento para "completar" filas incompletas, si ese es el problema que está teniendo. –

Respuesta

18

usando @ de datos de ejemplo de PaulHiemstra:

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

luego de tomar el cuidado de la AN como desee.

+1

Agregué su respuesta como una opción adicional a los puntos de referencia –

+0

Lazy yo-- esta fue mi respuesta en mi primer comentario, pero lo escribió con mucho más detalle –

+0

@Carl, +1 para su comentario karma. – BenBarnes

5

Lo que podría hacer es iterar sobre las líneas en el archivo, y solo agregar las líneas que tienen la longitud correcta.

que define el siguiente archivo csv prueba:

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

Usando read.table falla:

> 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 

ahora un enfoque 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 

Por supuesto esto es un ejemplo trivial como el el archivo es realmente pequeño Déjenos crear un ejemplo más desafiante para actuar como un punto de referencia.

# 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") 

ya por el punto de referencia

# 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 

lo tanto, el enfoque iterativo es un poco más lento, pero 20 segundos para 1 millón de filas podría ser aceptable (aunque esto depende de su definición aceptable). Especialmente cuando solo tiene que hacer esto una vez, y luego guardarlo usando save para una recuperación posterior. La solución sugerida por @Paolo es casi tan rápida como la llamada normal al read.table. Las filas que contienen una cantidad incorrecta de columnas (por lo tanto, NA) se eliminan utilizando complete.cases. Especificar qué clases son las columnas mejora aún más el rendimiento, y creo que este efecto será mayor cuando la cantidad de columnas y filas aumente.

Por lo tanto, en conclusión, la mejor opción es usar read.table con fill = TRUE, al tiempo que se especifican las clases de las columnas. El enfoque iterativo que usa ldply es solo una buena opción si desea más flexibilidad en la elección de cómo leer las líneas, p. solo lea la línea si un cierto valor está por encima de un umbral. Pero probablemente esto podría hacerse más rápido leyendo todos los datos en R, y luego creando un subconjunto. Solo cuando los datos son más grandes que tu RAM, podría imaginarme que el enfoque iterativo tiene sus méritos.

Cuestiones relacionadas