巨大な(3.5 GB)csvファイルをトリミングしてRに読み込む


87

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

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

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

どんなアイデアでも大歓迎です。

更新:

  • 制約
    • 私のマシンを使用する必要があるので、EC2インスタンスはありません
    • 可能な限りRのみ。この場合、速度とリソースは問題ではありません...私のマシンが爆発しない限り...
    • 以下に示すように、データには混合タイプが含まれていますが、後で操作する必要があります
  • データ
    • データは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]

データがRに収まるように、いくつかの列を切り取り、利用可能な40年(2009-2010から1980-2020)から2つを選びたいと思います。

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が最適であると判断しました。マレクがサンプル実装を提供したので、私はマレクにチェックをしました。

ここでの最終的な答えとして、マレックの実装を少し修正したバージョンを再現しました。strsplitとcatを使用して、必要な列のみを保持しています。

また、これは注意すべきであるMUCH Rが60ほどかかりますが、Pythonは5分で3.5ギガバイトのファイルを介してchompしのように...少ない効率的なPythonのより...しかし、あなたが持っているすべてが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に入れると、すべての文字ベクトルが削除されました。ただし、将来のために大きなデータセットを設計する必要がある場合は、このオプションを有効に保つためだけに数値のみを使用することを検討します。
  • スキャン
    • スキャンには、大きなメモリと同様のタイプの問題があるように見えましたが、readLinesのすべてのメカニズムがあります。要するに、今回は法案に適合しなかったのです。

3
基準が十分に単純な場合は、CSVの切り刻まれたバージョンを使用sedおよび/またはawk作成して、直接読み取ることができるようにすることができます。これは答えというよりは回避策なので、コメントとして残しておきます。
ハンクゲイ

あなたが仕事のための適切なツールを使用する必要があり、それは、単純なデータクリーニング/無関係な行を削除するかどう/列はソートのようなラインの流れツールをコマンド/ SED / awkは素晴らしいですし、あることを行っている-私はハンクに同意する方法以下Rより資源集約またはpython-ファイル形式のサンプルを提供すると、おそらく例を示すことができます
Aaron Statham 2010年

すごい。発見したことをお知らせください。
シェーン2010年

@ Hank&Aaron:私は通常、仕事に適したツールを使用することに専念していますが、これは仕事中のWindowsマシン上にあり、Rを学習していることを考えると、ベストプラクティスを無視して良い演習になると思いました。可能であれば、これをRとしてのみ試してください。
FTWynn 2010年

2
今後の参考のために、data.tableRパッケージを確認してください。このfread関数はread.table。よりもはるかに高速です。のようなものx = fread(file_path_here, data.table=FALSE)を使用して、data.frameオブジェクトとしてロードします。
paleo13 2016年

回答:


39

で試してみてください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)

これは私が今書いていたものとほぼ同じです。メモリの制約、混合タイプ、および不正な形式の行を考えると、これも最良の答えになると思います。
FTWynn 2010年

10

私はこれの専門家ではありませんが、MapReduceを試すことを検討してください。これは、基本的に「分割統治」アプローチを取ることを意味します。Rには、次のようないくつかのオプションがあります。

  1. mapReduce(純粋なR)
  2. RHIPEHadoopを使用); ファイルのサブセット化の例については、ドキュメントの例6.2.2参照してください。

あるいは、Rは、メモリの外部(ディスク上)に移動する大きなデータを処理するためのいくつかのパッケージを提供します。データセット全体をbigmemoryオブジェクトにロードし、R内で完全に削減を行うことができます。これを処理するための一連のツールについては、http: //www.bigmemory.org/を参照してください


良い提案ですが、MapReduceとその同類についてはあまり経験がありません。私はそれを読む必要があります。
FTWynn 2010年

bigmemoryその場合、最初に試す方が簡単かもしれません。
シェーン

10

Rで一度に1つずつファイルを読み込む同様の方法はありますか?

はい。readChar()関数は、彼らは、NULLで終了していると仮定することなく、文字のブロックに読み込みます。一度に1行のデータを読み取りたい場合は、readLines()を使用できます。ブロックまたは行を読み取り、操作を実行してからデータを書き出すと、メモリの問題を回避できます。ただし、AmazonのEC2で大きなメモリインスタンスを起動したい場合は、最大64GBのRAMを取得できます。これにより、ファイルに加えて、データを操作するための十分なスペースが確保されます。

より高速にする必要がある場合は、MapReduceを使用するというShaneの推奨事項は非常に優れています。ただし、EC2で大きなメモリインスタンスを使用するルートを選択する場合は、マシン上のすべてのコアを使用するためのマルチコアパッケージを確認する必要があります。

区切られたデータの多くのギグをRに読み込みたい場合は、少なくともRからsqldfに直接インポートし、R内からデータを操作できるsqldfパッケージを調べる必要があります。sqldfは1つであることがわかりました。この前の質問で述べたように、データのギグをRにインポートする最速の方法の1つです。


EC2インスタンスを念頭に置いておきますが、現時点ではデスクトップに固執する必要があり、2GBのRAMです。sqldfは間違いなく私が考えていたもののようです。ただし、不正な形式の行も窒息します(17列あるはずですが、数千行には1つしかありません)。それは他の前処理方法を必要としますか、それとも私が欠けているオプションがありますか?
FTWynn 2010年




5

使用readrread_*_chunked家族はどうですか?

だからあなたの場合:

testfile.csv

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
lol
Ada County;NC;2013;1;FIRE;Financial;Banks;82.5

実際のコード

require(readr)
f <- function(x, pos) subset(x, Year %in% c(2009, 2010))
read_csv2_chunked("testfile.csv", DataFrameCallback$new(f), chunk_size = 1)

これはf各チャンクに適用され、列名を記憶し、フィルタリングされた結果を最終的に組み合わせます。?callbackこの例のソースはどれかを確認してください。

その結果、次のようになります。

# A tibble: 2 × 8
      County State  Year Quarter Segment `Sub-Segment` `Sub-Sub-Segment`   GDP
*      <chr> <chr> <int>   <int>   <chr>         <chr>             <chr> <dbl>
1 Ada County    NC  2009       4    FIRE     Financial             Banks   801
2 Ada County    NC  2010       1    FIRE     Financial             Banks   825

増やすこともできますchunk_sizeが、この例では4行しかありません。



3

おそらく、MySQLまたはPostgreSQLに移行して、MSAccessの制限から身を守ることができます。

DBI(CRANで利用可能)ベースのデータベースコネクタを使用して、Rをこれらのシステムに接続するのは非常に簡単です。


より良いデータベースツールを使用するためのToucheですが、それは管理上の煩わしさを伴うため(大企業の管理規制が大好きです)、私は自分の持っているものに固執しようとしています。さらに、受け取ったテキストファイル間の変換をできるだけ少なくすることを目指しています。
FTWynn 2010年

3

scan()には、nlines引数とskip引数の両方があります。それを使用して、一度に行のチャンクを読み取り、日付をチェックして適切かどうかを確認できる理由はありますか?入力ファイルが日付順に並べられている場合は、スキップとnlinesを示すインデックスを保存して、将来のプロセスを高速化できます。


チェックアウトしますが、ファイルは日付などの役立つもので並べ替えられていません。プロバイダーは、特定の郡がどの地域にあるかで並べ替えることがより重要であると考えているようです
。/sigh

彼の提案を誤解したと思います。ファイルをチャンクごとに読み取り、各チャンクから必要な行だけを抽出します。ファイルを注文する必要はありません。
Karl Forner 2013

1

最近では、3.5GBはそれほど大きくはありません。Amazonクラウドで、244GB RAM(r3.8xlarge)を搭載したマシンに$ 2.80 /時間でアクセスできます。ビッグデータタイプのソリューションを使用して問題を解決する方法を理解するのに何時間かかりますか?あなたの時間はどれくらいの価値がありますか?はい、AWSの使用方法を理解するのに1〜2時間かかりますが、無料利用枠で基本を学び、データをアップロードし、最初の10k行をRに読み込んで機能することを確認してから、起動することができます。 r3.8xlargeのような大きなメモリインスタンスをすべて読み込んでください!ちょうど私の2c。


0

さて、2017年、私はsparkとsparkRに行くことを提案します。

  • 構文は、dplyrに似た単純な方法で記述できます。

  • それは小さなメモリに非常によく適合します(2017年の意味で小さい)

しかし、始めるのは恐ろしい経験かもしれません...


-3

DBを探してから、いくつかのクエリを実行して、DBIを介して必要なサンプルを抽出します。

3.5GBのcsvファイルをSQLiteにインポートしないでください。または、巨大なデータベースがSQLiteの制限に適合していることを少なくとも再確認してください。http://www.sqlite.org/limits.html

それはあなたが持っているひどい大きなDBです。スピードが必要な場合はMySQLを使用します。ただし、インポートが完了するまで何時間も待つ準備をしてください。あなたがいくつかの型破りなハードウェアを持っているか、あなたが未来から書いているのでない限り...

AmazonのEC2は、RとMySQLを実行しているサーバーをインスタンス化するための優れたソリューションになる可能性があります。

私の2つの謙虚なペニーの価値。


18
sqliteの3.5Gbはどのくらい大きいですか?適切なファイルシステムを使用している限り、問題はありません(シングルユーザーアプリケーションには30Gbを超えるsqliteデータベースを定期的に使用しています)
Aaron Statham
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.