计算两个 csv 文件之间差异的更快方法

Posted

技术标签:

【中文标题】计算两个 csv 文件之间差异的更快方法【英文标题】:Faster way to compute difference between two csv files 【发布时间】:2018-04-30 19:43:19 【问题描述】:

我正在尝试计算两个大型 csv 文件(约 4GB)之间的差异,以获得新添加的行并将它们写入输出 csv 文件。通过使用以下代码,我可以为相对较小的文件(~50MB)获得此功能。

input_file1 = "data.csv"
input_file2 = "data_1.csv"
output_path = "out.csv"

with open(input_file1, 'r') as t1, open(input_file2, 'r') as t2:
    fileone = t1.readlines()
    filetwo = t2.readlines()

with open(output_path, 'w') as outFile:
    for line in filetwo:
        if line not in fileone:
            outFile.write(line)

但是,对于较大的文件,上述代码要么太慢(运行大约半小时),要么因内存空间不足而崩溃。

有没有更快的方法来获取大型 csv 文件的差异?

【问题讨论】:

也许将数据转储到数据库中并进行重复数据删除?对于这种处理繁重的任务,4 GB 是一个巨大的文件。至少在数据库中,引擎应该能够稍微优雅地处理内存管理...... 尝试阅读这个答案:***.com/questions/44447107/… @Sneha,第二个 CSV 文件是否总是将新数据添加到文件的基础中?还是以不同的方式分类到数据中? 如果 .csv 文件都已排序,您可以使用类似整理器的合并排序。这具有 O(N+M) 的复杂性,其中你的双循环是 O(N*M) Plus:它只需要在内存中存储一​​个 M + 一个 N 记录。 @Grant - 第二个 csv 文件会将新数据添加到文件的底部。 【参考方案1】:

您不必完全阅读第二个文件,只需逐行阅读即可。

为了速度,只需从第一个文件中创建一个set(快速查找,如果有重复的行可以节省内存)。为此,您必须在编写结果时保持第二个文件处于打开状态:

input_file1 = "data.csv"
input_file2 = "data_1.csv"
output_path = "out.csv"

with open(input_file1, 'r') as t1:
    fileone = set(t1)

with open(input_file2, 'r') as t2, open(output_path, 'w') as outFile:
    for line in t2:
        if line not in fileone:
            outFile.write(line)
for line in t2 逐行读取文件(尽可能避免使用readlines()),因此即使文件很大,内存占用也很小。 fileone 确实需要一些内存,但希望如果它更小和/或有重复的行,则不会那么多,当然比readlines()if line not in fileone 可能看起来和以前一样,但它具有平均 O(1) 复杂度,这使得程序更快

【讨论】:

【参考方案2】:

您可以使用数据库或排序合并。我会给你基本算法(而不是python代码)

排序合并说明

这个想法是将 2 个文件排序为相同的顺序。然后依次读取2个文件

如果两个文件中的记录相等 --> 在两个文件中 如果旧文件记录 > 新文件记录 --> 记录已插入 如果旧文件记录 记录已被删除

排序合并算法

Sort the 2 files to new SortedFiles using the Operating Systems sort 
(use the whole record as sort key)

Open/Read  SortedOldFile
Open/Read  SortedNewFile

while (not end-of-file-SortedOldFile) and (not end-of-file-SortedOldFile):
    if SortedOldFile.record < SortedNewFile.record:
       ## Deleted processing goes here
       read SortedOldFile
    elseif SortedOldFile.record > SortedNewFile.record:
       ## Insert processing  goes here
       read SortedNewFile
    else
       read SortedOldFile
       read SortedNewFile

while (not end-of-file-SortedOldFile):
   ## Deleted processing
   read SortedOldFile

while (not end-of-file-SortedNewFile):
   ## Insert processing
   read SortedNewFile

优点:

使用最少的内存 它可以扩展到绝对庞大的文件 应该足够快,操作系统排序非常高效

缺点:

使用额外的磁盘空间(现在磁盘空间很便宜) 代码取决于操作系统

【讨论】:

【参考方案3】:

您可以对行进行散列以压缩较小的集合,然后进行比较。 或者使用更高级的算法来查找指纹

https://en.wikipedia.org/wiki/Fingerprint_(computing)

import hashlib
input_file1 = "data.csv"
input_file2 = "data_1.csv"
output_path = "out.csv"

def get_data(file_):
    res = 
    m = hashlib.md5()
    for i, line in file_:
      hashed_line = m.update(line).hexdigest()
      if hashed_line not in res:
        res[hashed_line ] = []
      res[hashed_line ].append(i)


with open(input_file1, 'r') as t1, open(input_file2, 'r') as t2:
    file1_data =  get_data(t1)
    file2_data =  get_data(t2)
    file2_raw = t2.readlines()


with open(output_path, 'w') as outFile:
    for line in file2_data:
        if line not in file1_data:
            outFile.write(file2_raw[file2_data[line]])

【讨论】:

oops for line in file2_data: if line not in file2_data: 再次 :) 是时候测试您的代码了 :) 我会谨慎使用哈希值。甚至 md5 也不是双射的。如果哈希不匹配,那么好吧,它是不同的,但我认为如果哈希匹配你必须测试值...... 散列的东西很有趣,但它不会节省内存,因为您仍然需要原始数据来写回文件。此外,如果哈希匹配,您仍然需要比较实际的字符串值。所以不确定你是否能赢得时间。 @Jean-FrançoisFabre 它没有比较字符串。它比较字典中的散列字符串,所以它是 O(1) 速度。对于内存使用,它应该小于 op 的 @Jean-FrançoisFabre 字典中的散列字符串应小于内存中的字符串散列(集) 是的,但我的意思是你可以使用md5.hexdigest(s1) == md5.hexdigest(s2)s1 != s2。不太可能,但有可能。

以上是关于计算两个 csv 文件之间差异的更快方法的主要内容,如果未能解决你的问题,请参考以下文章

如何计算两个日期之间的差异?

以小时和分钟计算两个日期之间的差异

在 Swift 中计算两个日期之间的差异

Java 8:计算两个 ZonedDateTime 之间的差异

使用python查找两个csv文件列之间的差异

如何计算不同时区的两个 NSDate 对象之间的天数差异?