你可以做的是遍歷文件中的行,並只添加具有正確長度的行。
我定義了以下測試csv文件:
1;2;3;4
1;2;3;4
1;2;3
1;2;3;4
使用read.table
失敗:
> 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
現在的迭代方法:
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
Ofcourse這是一個簡單的例子爲文件真的很小。讓我們創建一個更具挑戰性的例子來作爲基準。
# 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")
現在爲基準
# 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
所以迭代的方法是相當慢一點,但20秒一個百萬行是可以接受的(雖然這取決於你的可接受的定義)。特別是當你只需要這一次,並且使用save
保存以備以後檢索時。 @Paolo建議的解決方案几乎和正常呼叫read.table
一樣快。使用complete.cases
排除包含錯誤列數的行(因此爲NA
's)。指定列的哪些類進一步提高了性能,並且我認爲當列和行的數量變大時,這種影響會變得更大。
因此總而言之,最好的選擇是使用read.table
和fill = TRUE
,同時指定列的類。使用ldply
的迭代方法只是一個很好的選擇,如果你想在選擇如何讀取線條方面有更大的靈活性,例如只有在特定值高於閾值時纔讀取該行。但是,可能通過將所有數據讀入R來更快地完成此操作,而不是創建子集。只有當數據比RAM更大時,我才能想象迭代方法有其優點。
你的定義有什麼不好? –
任何你不直接使用'read.table'的理由?它有很多參數來選擇和忽略各種「壞」字符。如果這是您遇到的問題,還有一個參數來填充不完整的行。 –