1

多くの重複する列を持つ多数の csv ファイルを rbind しようとしています。

library(plyr)

filenames <- list.files("output/", pattern = "*.csv")
filenames = paste("output/", filenames, sep="")
frames = lapply(filenames,read.csv,header=TRUE)
input = do.call(rbind.fill,frames)
write.csv(input, file="stacked.csv", quote=FALSE, na="", row.names=FALSE)

結合されたフレームには約 300,000 行があり、列の結合は約 3,000 で、104 個のファイルがあります。これを行うと、コンピューターがメモリ制限 (32 GB) を超えて吹き飛ばされ、R がクラッシュします。また、セグメント化されたメモリを処理するためにプロセスを分割しようとしました。繰り返しますが、運が悪いです:

library(plyr)

filenames <- list.files("output/", pattern = "*.csv")
filenames = paste("output/", filenames, sep="")
input = lapply(filenames,read.csv,header=TRUE)
part1 = do.call(rbind.fill,input[1:30])
save(part1,part2,file="p1")
part2 = do.call(rbind.fill,input[31:70])
part3 = do.call(rbind.fill,input[71:104])

write.table(input, file="stacked.csv", quote=FALSE, na="", row.names=FALSE)

上記は私が行ったことのほんの一例です。part1、part2、part3 をメモリにロードすると、合計で 6 GB になります。保存してロードする前のメモリ使用量は約 20 GB です。次に試してみるrbind.fill(part1,part2)と、メモリ使用量が再び 32 GB を超えて急増します。

これにアプローチする方法について何か提案はありますか? 私は他のソリューション(python、sqlなど)を受け入れています。

4

2 に答える 2

1

これが私が使用したPythonコードです。また、ファイル名の列を追加し、floatに変換できないもの(特にテキストフィールド)を削除し、出力ファイルに書き込むときに1行(ヘッダーを含めて2行)をスキップします。

import csv
import glob

files = glob.glob("data/*.txt")
csv.field_size_limit(1000000000)

outfile = "output.csv"

def clean_row(row,filename):
    for k,v in row.items():
        try:
            row[k] = float(v)
        except:
            row[k] = ""
    row['yearqtr'] = filename
    return row

headers = set()
for filename in files:
    with open(filename,"r") as infile:
        reader = csv.reader(infile)
        for header in next(reader):
            headers.add(header)

headers = list(headers)
headers.insert(0,"yearqtr")

with open(outfile, "w") as outfile:
    writer = csv.DictWriter(outfile,headers,restval="",extrasaction="ignore")
    writer.writeheader()
    for filename in files:
        with open(filename, "r") as infile:
            reader = csv.DictReader(infile)
            next(reader)
            writer.writerows((clean_row(row,filename) for row in reader))
于 2012-11-16T09:02:59.867 に答える
1

非常に少ないメモリを使用し、代わりにディスクを使用するアルゴリズムは次のようになります。

  • 1) すべてのファイルのヘッダーを読み取って、一意の列セットを見つけます。
  • NA2) 各ファイルを 1 行ずつ処理します。不足している列に s を追加し、その行を 1 つの大きなファイルに書き込みます。

完了したら、その大きなファイルを R セッションに読み込むことができます (大きすぎない場合)。

この種のタスクには、他の言語の方が適している場合があります。パールが思い浮かびます。

編集: 興味がある場合は、Perl を使用したコードを次に示します。それをrbindfill.plファイルに入れて、次のように実行します。perl rindfill.pl > output.csv

use strict;
use warnings;

my @files = glob "output/*.csv";
my %fields;

foreach my $file (@files)
   {
   open my $fh, '<', $file;
   my $header = <$fh>;
   chomp $header;
   map {$fields{$_} = 1} split ',', $header;
   close $fh;
   }

my @all_fields = keys %fields;
print join(',', @all_fields) . "\n";

foreach my $file (@files)
   {
   open my $fh, '<', $file;
   my $header = <$fh>;
   chomp $header;
   my @fields = split ',', $header;
   foreach my $line (<$fh>)
      {
      chomp $line;
      my $rec;
      @{$rec}{@fields} = split ',', $line;
      print join(',', map { defined $rec->{$_} ? $rec->{$_} : ''} @all_fields) . "\n";
      }
   close $fh;
   }
于 2012-11-16T00:52:59.280 に答える