2010-09-21 163 views
1

我有一个有趣的问题。如何使用Python比较2个非常大的矩阵

我有一个非常大的(大于300MB,超过10,000,000行/行的文件)CSV文件与时间序列数据点内。每个月我都会得到一个新的CSV文件,与之前的文件几乎相同,只是添加和/或删除了一些新行,并且可能修改了一些行。

我想使用Python来比较2个文件,并确定哪些行已被添加,删除和修改。

问题是该文件非常大,所以我需要一个能够处理大文件大小并在合理时间内有效执行的解决方案,越快越好。一个什么样的文件及其新文件可能看起来像

例子:

旧文件
A,2008-01-01,23
A,2008-02-01,45
B,2008-01-01,56
B,2008-02-01,60
C,2008-01-01,3
C,2008-02-01,7
C,2008-03-01,9
etc...

的新文件
A,2008-01-01,23
A,2008-02-01,45
A,2008-03-01,67(添加)
B,2008-01-01,56
B,2008-03-01,33(移除并添加)
C,2008-01-01,3
C,2008-02-01,7
C,2008-03-01,22(经修饰)
etc...

基本上2个文件可以被看作是需要比较矩阵,我已经开始使用PyTable的思维。任何想法如何解决这个问题将不胜感激。

+1

您能给我们介绍一下你们之间是如何决定多一点信息“修改“或”添加“?它是基于日期吗? – JudoWill 2010-09-21 13:58:14

+1

对不起,“修改”我的意思是只有最后一列值的变化。因此,series_id(第一列)和日期(第二列)保持不变。 – sizeight 2010-09-21 14:02:42

回答

4

像这样。

第1步。排序。

第2步。读取每个文件,进行逐行比较。将差异写入另一个文件。

您可以轻松地自己写。或者你可以使用difflibhttp://docs.python.org/library/difflib.html

请注意,一般的解决方案很慢,因为它搜索差异附近的匹配行。编写自己的解决方案可以运行得更快,因为您知道文件应该如何匹配。您可以优化“差异化后再同步”算法。

而10,000,000行几乎没有问题。这不是那么大。两个300Mb文件很容易放入内存。

1

这是一个天真的实现一点点,但会处理未排序的数据:

import csv 

file1_dict = {} 
file2_dict = {} 

with open('file1.csv') as handle: 
    for row in csv.reader(handle): 
     file1_dict[tuple(row[:2])] = row[2:] 

with open('file2.csv') as handle: 
    for row in csv.reader(handle): 
     file2_dict[tuple(row[:2])] = row[2:] 

with open('outfile.csv', 'w') as handle: 
    writer = csv.writer(handle) 
    for key, val in file1_dict.iteritems(): 
     if key in file2_dict: 
      #deal with keys that are in both 
      if file2_dict[key] == val:   
       writer.writerow(key+val+('Same',)) 
      else: 
       writer.writerow(key+file2_dict[key]+('Modified',)) 
      file2_dict.pop(key) 
     else: 
      writer.writerow(key+val+('Removed',)) 
    #deal with added keys! 
    for key, val in file2_dict.iteritems(): 
     writer.writerow(key+val+('Added',)) 

你可能无法这一解决方案“在下降”,但它应该让你的〜95%那里的路。 @ S.Lott是对的,2个300mb的文件很容易放入内存中......如果你的文件进入1-2gb范围,那么这可能需要根据排序后的数据进行修改。

事情是这样的接近......虽然你可能不得不改变周围的比较,为加入了修改,使感:

#assumming both files are sorted by columns 1 and 2 
import datetime 
from itertools import imap 

def str2date(in): 
    return datetime.date(*map(int,in.split('-'))) 

def convert_tups(row): 
    key = (row[0], str2date(row[1])) 
    val = tuple(row[2:]) 
    return key, val 

with open('file1.csv') as handle1: 
    with open('file2.csv') as handle2: 
     with open('outfile.csv', 'w') as outhandle: 
      writer = csv.writer(outhandle) 
      gen1 = imap(convert_tups, csv.reader(handle1)) 
      gen2 = imap(convert_tups, csv.reader(handle2)) 
      gen2key, gen2val = gen2.next()  
      for gen1key, gen1val in gen1: 
       if gen1key == gen2key and gen1val == gen2val: 
        writer.writerow(gen1key+gen1val+('Same',)) 
        gen2key, gen2val = gen2.next() 
       elif gen1key == gen2key and gen1val != gen2val: 
        writer.writerow(gen2key+gen2val+('Modified',)) 
        gen2key, gen2val = gen2.next() 
       elif gen1key > gen2key: 
        while gen1key>gen2key: 
         writer.writerow(gen2key+gen2val+('Added',)) 
         gen2key, gen2val = gen2.next() 
       else: 
        writer.writerow(gen1key+gen1val+('Removed',))