7

大量のデータを含む大きなファイルがあり、それをデータフレームに読み込みたいのですが、無効な行がいくつか見つかりました。これらの無効な行により、read.tableが破損します。次の方法で無効な行をスキップしようとしましたが、パフォーマンスが非常に悪いようです。

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

これを達成するためのより良い方法はありますか?ありがとう

4

2 に答える 2

19

@PaulHiemstraのサンプルデータを使用:

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

次に、必要に応じてNAを処理します。

于 2012-05-15T11:49:33.390 に答える
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

もちろん、ファイルが本当に小さいので、これは簡単な例です。ベンチマークとして機能する、より挑戦的な例を作成しましょう。

# 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

したがって、反復アプローチはかなり遅くなりますが、100万行に対して20秒が許容される場合があります(ただし、これは許容範囲の定義によって異なります)。特に、これを1回だけ行う必要がある場合はsave、後で取得するために使用して保存します。@Paoloによって提案された解決策は、への通常の呼び出しとほぼ同じくらい高速read.tableです。間違った量の列(したがってNA's)を含む行は、を使用して削除されcomplete.casesます。列のクラスを指定するとパフォーマンスがさらに向上します。列と行の数が増えると、この効果は大きくなると思います。

したがって、結論として、最良のオプションは、列のクラスを指定するときに、で使用read.tableすることです。fill = TRUEを使用する反復アプローチldplyは、行の読み取り方法をより柔軟に選択する場合にのみ適切なオプションです。たとえば、特定の値がしきい値を超えている場合にのみ行を読み取ります。しかし、おそらくこれは、サブセットを作成するよりも、すべてのデータをRに読み込むことでより迅速に実行できます。データがRAMよりも大きい場合にのみ、そのメリットがある反復アプローチを想像することができました。

于 2012-05-15T11:32:34.460 に答える