2012-05-15 145 views
7

我有一個包含大量數據的大文件,我想將它讀入數據框,但發現一些無效行。這些無效行導致read.table中斷。我嘗試下面的方法來跳過無效的行,但它看起來表現非常糟糕。如何從R中的文件中讀取數據幀時跳過無效行?

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

有沒有更好的方法來實現這個目標?謝謝

+2

你的定義有什麼不好? –

+1

任何你不直接使用'read.table'的理由?它有很多參數來選擇和忽略各種「壞」字符。如果這是您遇到的問題,還有一個參數來填充不完整的行。 –

回答

18

使用@ PaulHiemstra的樣本數據:

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

那麼,你想照顧的NAS。

+1

我把你的答案作爲基準的額外選項 –

+0

懶惰的我 - 這是我在我的第一個評論中的回答,但是你寫出來的更好的細節 –

+0

@Carl,+1爲你的評論業力。 – BenBarnes

5

你可以做的是遍歷文件中的行,並只添加具有正確長度的行。

我定義了以下測試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.tablefill = TRUE,同時指定列的類。使用ldply的迭代方法只是一個很好的選擇,如果你想在選擇如何讀取線條方面有更大的靈活性,例如只有在特定值高於閾值時纔讀取該行。但是,可能通過將所有數據讀入R來更快地完成此操作,而不是創建子集。只有當數據比RAM更大時,我才能想象迭代方法有其優點。

相關問題