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

在Python中优化删除大文件中的重复项,python,optimization,duplicates,large-files,Python,Optimization,Duplicates,Large Files,我有一个非常大的文本文件(27GB),我正试图通过删除第二个数据库中重复的行来缩小它,该数据库有几个大小更合理的文件(500MB-2GB)。我有一些功能代码,我想知道的是,有没有任何方法可以优化这些代码,使其运行得更快,人类时钟时间?目前,在使用1.5GB输入和500MB滤波器的小型测试运行中,这需要75秒才能完成 我已经经历了很多次这种想法的迭代,这一次是最适合时间的,如果有人有为过滤器制作更好的逻辑结构的想法,我很乐意听到,过去的尝试都比这一次更糟糕:将过滤器加载到一个集合中,并在输入中循环

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

我已经经历了很多次这种想法的迭代,这一次是最适合时间的,如果有人有为过滤器制作更好的逻辑结构的想法,我很乐意听到,过去的尝试都比这一次更糟糕:将过滤器加载到一个集合中,并在输入中循环搜索重复项(大约是这一次的一半),将输入加载到一个集合中,并通过差异更新运行过滤器(速度几乎与此相同,但也与我想要的相反),然后将输入和过滤器分块加载到集合中,并执行集合差异(这是一个可怕的、可怕的想法,可能会奏效)如果我的过滤器更小,那么我也不必拆分它们。)

所以,这些都是我尝试过的。所有这些进程在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()
def分拣过滤器(输入、过滤器、输出):
文件输入=打开(输入,'r')
文件\过滤器=打开(过滤器,'r')
文件输出=打开(输出,'w')
inline=file\u input.next()
filterline=file\u filter.next()
尝试:
内联和过滤线时:
如果inline过滤器线:
filterline=file\u filter.next()
持续
如果inline==过滤器行:
filterline=file\u filter.next()
inline=file\u input.next()
除停止迭代外:
文件\u output.writelines(文件\u input.readlines())
最后:
文件_filter.close()
文件_input.close()
文件_output.close()

通过执行
cmp(inline,filterline)
,每行只能执行一次字符串比较操作:

  • -1
    表示
    内联
  • 0
    表示
    inline==filterline
  • +1
    表示
    内联
这可能会给你额外的百分之几。比如:

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

看到您的输入文件已排序,下面的操作应该可以正常工作。heapq的merge从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)

我很想知道这两者之间的比较;它基本上只是在玩一点迭代的顺序

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())
def sortedfilter(输入名称、输入名称、输出名称):
将open(in_fname)作为inf,将open(filter_fname)作为fil,将open(out_fname,'w')作为outp:
ins=inf.next()
尝试:
财政司司长:
当ins
考虑以二进制方式打开文件,这样就不需要转换为unicode:

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

我喜欢这个,我对它进行了测试,结果表明,将它写入内存并读取1-3次实际上比重复计算要慢。不过,在内存更好的系统上,这可能会更快。@Briana真的吗?我很惊讶。你有没有试过用cProfile编写这段代码,看看它花了多少时间?我以前从来没有用过cProfile,所以我不确定我是否能用它收集到比这更多的数据,但这是两个结果,有cmp和没有cmp。当我试着用输入运行它时,我得到了一个错误,我以前没有玩过heapq,所以我不知道它的输入/输出是什么样子的,所以我不知道它为什么会坏。你运行的是什么版本的python?2.7,我实际上刚刚开始工作,在你最后一次编辑后再次抓取它,我不确定你是不是改变了代码,除了输入,还是我之前把事情搞砸了。我想你误解了我想做的,我不想合并文件,这样做,删除重复项,我想从一个文件中删除重复项,而另一个文件保持完整和独立。我明白了。我现在修改了代码,以确保在合并过程中不会拾取来自过滤器文件的记录。这涉及使用itertools.repeat将特定于文件的编号与输入文件中的每条记录相关联,然后根据该编号进行过滤,以确保仅将输入文件中的记录写入输出文件;记录