1. ホーム
  2. r

[解決済み] 3.5GBの巨大なcsvファイルをRに読み込むためにトリミングする。

2023-04-12 21:14:22

質問

詳細で不完全な行を持つデータ ファイル (セミコロンで区切られたもの) があります (Access と SQL が停止してしまいます)。これは、40 年間の郡レベルのデータ セットで、セグメント、サブセグメント、およびサブサブセグメント (合計で約 200 の要素) に分割されています。要するに、巨大で、単純に読もうとするとメモリに収まらないのです。

そこで私の質問は、すべての郡が欲しいが、単一の年だけ(そしてセグメントの最高レベルだけ...最終的に約 100,000 行につながる)、R にこのロールアップを得るために行く最良の方法は何でしょうか?

現在、私は Python で無関係な年を切り出そうとしており、一度に 1 行を読んで操作することによってファイルサイズの制限を回避していますが、R 専用のソリューション (CRAN パッケージ OK) を希望しています。Rで一度にファイルを読み込むための同様の方法はありますか?

どんなアイデアでも、非常に感謝されます。

更新しました。

  • 制約事項
    • 使用する必要があるのは 私の マシンを使用する必要があるため、EC2インスタンスは不要
    • 可能な限り R-only にします。スピードとリソースはこの場合関係ありません...私のマシンが爆発しない限り...
    • 下にあるように、データには型が混在しているので、後で操作する必要があります。
  • データ
    • データは3.5GB、約850万行17列
    • 数千行 (~2k) が不正であり、17 カラムではなく 1 カラムのみである。
      • これらはまったく重要ではないので、削除することができます。
    • このファイルから必要なのは ~100,000 行だけです (以下を参照してください)

データの例です。

County; State; Year; Quarter; Segment; Sub-Segment; Sub-Sub-Segment; GDP; ...
Ada County;NC;2009;4;FIRE;Financial;Banks;80.1; ...
Ada County;NC;2010;1;FIRE;Financial;Banks;82.5; ...
NC  [Malformed row]
[8.5 Mill rows]

私はいくつかの列を切り出し、40の利用可能な年(1980-2020から2009-2010)から2つを選び、データがRに適合できるようにしたいです。

County; State; Year; Quarter; Segment; GDP; ...
Ada County;NC;2009;4;FIRE;80.1; ...
Ada County;NC;2010;1;FIRE;82.5; ...
[~200,000 rows]

結果です。

提案されたすべてのものをいじった後、JDとMarekによって提案されたreadLinesが最もうまくいくと判断しました。彼がサンプルの実装を与えたので、私は Marek にチェックを入れました。

私は最終的な答えのために、Marekの実装を少し修正したものをここに再現しました。strsplitとcatを使って、私が欲しい列だけを保持します。

また、これは MUCH Pythonよりも効率的ではありません...例えば、Pythonは3.5GBのファイルを5分で切り刻み、Rは約60分かかります...しかし、Rしか持っていなければ、これはチケットです。

## Open a connection separately to hold the cursor position
file.in <- file('bad_data.txt', 'rt')
file.out <- file('chopped_data.txt', 'wt')
line <- readLines(file.in, n=1)
line.split <- strsplit(line, ';')
# Stitching together only the columns we want
cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
## Use a loop to read in the rest of the lines
line <- readLines(file.in, n=1)
while (length(line)) {
  line.split <- strsplit(line, ';')
  if (length(line.split[[1]]) > 1) {
    if (line.split[[1]][3] == '2009') {
        cat(line.split[[1]][1:5], line.split[[1]][8], sep = ';', file = file.out, fill = TRUE)
    }
  }
  line<- readLines(file.in, n=1)
}
close(file.in)
close(file.out)

アプローチによる失敗例。

  • sqldf
    • もしデータが整形されていれば、将来この種の問題に使うのは間違いなくこれでしょう。しかし、そうでない場合は、SQLiteは窒息します。
  • MapReduce
    • 正直なところ、ドキュメントを見るとちょっと怖くて、なかなか試せなかったんです。オブジェクトがメモリ内にあることが必要なように見えましたが、もしそうだとしたら、それは意味がありません。
  • bigmemory
    • この方法はきれいにデータにリンクしていますが、一度に1つのタイプしか扱えません。その結果、私の文字ベクトルはすべて big.table に入れると落ちてしまいました。しかし、将来的に大きなデータセットを設計する必要がある場合は、このオプションを維持するために、数値のみを使用することを検討します。
  • スキャン
    • Scan は big memory と同じようなタイプの問題があるようですが、readLines のすべてのメカニックを備えています。要するに、今回はその条件に当てはまらなかったということです。

どのように解決するのか?

私の試みは readLines . このコードでは csv を作成しています。

file_in <- file("in.csv","r")
file_out <- file("out.csv","a")
x <- readLines(file_in, n=1)
writeLines(x, file_out) # copy headers

B <- 300000 # depends how large is one pack
while(length(x)) {
    ind <- grep("^[^;]*;[^;]*; 20(09|10)", x)
    if (length(ind)) writeLines(x[ind], file_out)
    x <- readLines(file_in, n=B)
}
close(file_in)
close(file_out)