2012-11-16 53 views
1

我試圖對大量含有許多重疊列的csv文件進行rbind。rbind.fill large data.frames

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行,列的聯合是3000左右,並有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。

關於如何解決這個問題的任何建議?我打開其他解決方案(蟒蛇,SQL等)。

+0

您是否事先知道列名和類的完整列表? – mnel

+1

我的第一本能是嘗試Python,因爲您不必將文件讀入內存中就可以對其進行操作。您可以迭代一次文件以構建列名稱列表,然後再次通過它們來實際讀取和連接數據。 – Marius

回答

1

使用內存非常低,但磁盤上,而將是一個算法:

  • 1)讀取的所有文件的標題,以瞭解該組唯一的列,
  • 2)流程中的每個文件一行行:將NA s添加到缺少的列並將該行寫入一個大文件。

只有當你完成,你可以讀取大文件到您的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; 
    } 
+0

謝謝,我會一起扔蟒蛇的東西,因爲我更清楚這一點。應該很容易使用 http://docs.python.org/2/library/csv.html#csv.DictWriter 我會將代碼發回到這裏,一旦我花了幾分鐘把它放在一起。 –

+0

感謝您的Perl代碼。只是爲了踢我寫了一個python腳本來做到這一點,我在下面發佈。 –

+0

原始列順序不會保留在此腳本的輸出中...我如何實現此目的? –

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