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.
Cosa c'è di male nella tua definizione? –
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. –