优化在Python中删除大文件中的重复项

时间:2012-07-07 19:50:35

标签: python optimization duplicates large-files

我有一个非常大的文本文件(27GB),我试图通过删除在第二个数据库中复制的行来缩小,这个数据库有几个更合理的文件(500MB-2GB)。我有一些功能代码,我想知道的是有什么方法可以优化这段代码来运行更快,人类的时钟时间?目前,在1.5GB输入和500MB过滤器的小型测试运行中,这需要75秒才能完成。

我已经经历了这个想法的多次迭代,这个目前最好的时间,如果有人有想法为过滤器做出更好的逻辑结构我很乐意听到它,过去的尝试都变得更糟比这一个:将过滤器加载到一个集合中并循环通过输入搜索重复项(大约是此速度的一半),将输入加载到一个集合中并通过difference_update运行过滤器(几乎与此一样快但也在执行反转我想要的东西,并将输入和过滤器加载到块中的集合并进行设置差异(这是一个可怕的,可怕的想法,可能会工作(也许?)如果我的过滤器较小,所以我不必拆分它们也是。)

所以,这些都是我尝试过的。所有这些进程都在CPU上最大化,我的最终版本运行在大约25-50%的磁盘I / O,过滤器和输出在一个物理磁盘上,输入在另一个物理磁盘上。我正在运行双核心,并且不知道这个特定的脚本是否可以被线程化,之前从未进行任何多线程处理,如果可能的话,我希望能指向正确的方向。

有关数据的信息!如前所述,输入比滤波器大许多倍。我期待重复的比例非常小。数据在行中,所有行都长度不超过20个ASCII字符。这些文件都已排序。

我已经改变了三个逻辑语句的顺序,基于期望独特的输入行将是大多数行,然后是唯一的过滤器,然后是重复的,这是在“没有重复”的“最佳”情况下在任何时候,我节省了大约10%的时间。

有什么建议吗?

def sortedfilter(input,filter,output):
    file_input = open(input,'r')
    file_filter = open(filter,'r')
    file_output = open(output,'w')
    inline = file_input.next()
    filterline = file_filter.next()
    try:
        while inline and filterline:
            if inline < filterline:
                file_output.write(inline)
                inline = file_input.next()
                continue
            if inline > filterline:
                filterline = file_filter.next()
                continue
            if inline == filterline:
                filterline = file_filter.next()
                inline = file_input.next()
    except StopIteration:
        file_output.writelines(file_input.readlines())
    finally:
        file_filter.close()
        file_input.close()
        file_output.close()

4 个答案:

答案 0 :(得分:1)

通过执行cmp(inline, filterline)

,每行只能执行一次字符串比较操作
  • -1表示inline < filterline
  • 0表示inline == filterline
  • +1表示inline < filterline

这可能会让你获得额外的百分比。如下所示:

    while inline and filterline:
        comparison = cmp(inline, filterline)
        if comparison == -1:
            file_output.write(inline)
            inline = file_input.next()
            continue
        if comparison == 1:
            filterline = file_filter.next()
            continue
        if comparison == 0:
            filterline = file_filter.next()
            inline = file_input.next()

答案 1 :(得分:1)

看到您的输入文件已排序,以下内容应该有效。 heapq的合并从groupby操作的已排序输入生成一个已排序的流;长度大于1的组将被丢弃。这种基于流的方法应具有相对较低的内存要求

from itertools import groupby, repeat, izip
from heapq import merge
from operator import itemgetter

with open('input.txt', 'r') as file_input, open('filter.txt', 'r') as file_filter, open('output.txt', 'w') as file_output:
  file_input_1 = izip(file_input, repeat(1))
  file_filter_1 = izip(file_filter, repeat(2))
  gen = merge(file_input_1, file_filter_1)
  gen = ((k, list(g)) for (k, g) in groupby(gen, key=itemgetter(0)))
  gen = (k for (k, g) in gen if len(g) == 1 and g[0][1]  == 1)
  for line in gen:
    file_output.write(line)

答案 2 :(得分:1)

我有兴趣知道这是如何比较的;它基本上只是在尝试迭代次序。

def sortedfilter(in_fname, filter_fname, out_fname):
    with open(in_fname) as inf, open(filter_fname) as fil, open(out_fname, 'w') as outf:
        ins = inf.next()
        try:
            for fs in fil:
                while ins < fs:
                    outf.write(ins)
                    ins = inf.next()
                while ins == fs:
                    ins = inf.next()
        except StopIteration:
            # reached end of inf before end of fil
            pass
        else:
            # reached end of fil first, pass rest of inf through
            file_output.writelines(file_input.readlines())

答案 3 :(得分:0)

考虑将文件打开为二进制文件,因此不需要转换为unicode:

with open(in_fname,'rb') as inf, open(filter_fname,'rb') as fil, open(out_fname, 'wb') as outf: