1

我正在尝试 rbind 具有许多重叠列的大量 csv 文件。

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 个文件。这样做我的电脑超过了它的内存限制 (32gb) 并且 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)

以上只是我所做的一个示例。一旦我将第 1 部分、第 2 部分、第 3 部分加载回内存,它们总共约为 6gb。在保存和加载之前,我的内存使用量约为 20gb。然后我尝试rbind.fill(part1,part2),内存使用量再次飙升 > 32gb。

关于如何解决这个问题的任何建议?我对其他解决方案(python、sql 等)持开放态度。

4

2 回答 2

1

使用非常低的内存但使用磁盘的算法将是:

  • 1)读取所有文件的标题以找到唯一的列集,
  • 2)逐行处理每个文件:将NAs 添加到缺少的列并将该行写入一个大文件。

只有完成后,您才能将该大文件读入您​​的 R 会话(如果它不是太大的话。)

其他语言可能更适合此类任务。Perl 浮现在脑海中。

编辑:如果您有兴趣,这里是使用 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 回答
1

这是我使用的python代码。它还为文件名添加一列,删除任何无法转换为浮点数的内容(特别是文本字段),并在写入输出文件时跳过一行(包括标题在内的 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 回答