rbind.fill大数据.frames

时间:2012-11-16 00:00:21

标签: r csv plyr

我正在试图修复大量具有许多重叠列的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)

以上只是我所做的一个例子。一旦我将part1,part2,part3加载回内存,它们总共约为6gb。在保存和加载之前,我的内存使用量约为20GB。然后我再次尝试rbind.fill(part1,part2)并且内存使用率高峰> 32gb。

有关如何处理此问题的任何建议?我对其他解决方案(python,sql等)持开放态度。

2 个答案:

答案 0 :(得分:1)

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

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

只有当你完成后,你才能将那个大文件读入你的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;
   }

答案 1 :(得分:1)

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

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))