首页 > 解决方案 > Python:在包含数百万数据的文件中查找重复项的性能问题

问题描述

我正在使用 core-python API 在 python 2.7 中为我的项目编写详细的文件验证脚本。这是为了比较另一个 ETL 代码的源文件和目标文件。这包括逐行元数据验证、计数验证、重复检查、空值检查和完整数据验证。我已经完成了脚本并且它对 100k 数据集运行良好(我在 100k、200k 卷上进行了一些测试)。但是如果我运行重复检查的方法将永远运行(我的意思是要花费大量时间)数百万数据。我已经调试了代码,发现下面的重复检查方法导致了问题。

    def dupFind(dup_list=[],output_path=""):
        #dup_list is the list containing duplicates. Actually this is the list of contents of a file line by line as entries
        #output_path is the path to which output records and respective duplicate count of each records are saved as a single file
        #duplicates is a set which contains tuples with two elements each in which first element is the duplicated record and second is the duplicated count

        duplicates=set((x,dup_list.count(x)) for x in filter(lambda rec : dup_list.count(rec)>1,dup_list)) 
        print "time taken for preparing duplicate list is {}".format(str(t1-t0))
        dup_report="{}\dup.{}".format(output_path, int(time.time()))
        print "Please find the duplicate records  in {}".format(dup_report)
        print ""
        with open(dup_report, 'w+') as f:
            f.write("RECORD|DUPLICATE_COUNT\n")
            for line in duplicates:
                f.write("{}|{}\n".format(line[0], line[1]))

首先,我正在读取文件并将其转换为如下所示的列表(运行速度很快):

     with open(sys.argv[1]) as src,open(sys.argv[2]) as tgt:
            src = map(lambda x : x.strip(),list(src))
            tgt = map(lambda x : x.strip(),list(tgt))

之后,我在“src”和“tgt”列表上应用以下逻辑(提供伪代码)以查找文件是否重复:

    #here output path is passed as a user argument while running the script

    if len(set(tgt)) < len(tgt) then Target  is duplicated and call dupFind function as dupFind(tgt,outputpath)
    if len(set(src)) < len(src) then source is duplicated and call dupFind function as dupFind(src,outputpath)

因此,哪个列表被重复,将由 dupFind 函数使用,然后它将重复的记录和相应的计数以“dup.epochtime”格式保存到输出路径中的文件中。如果我为数百万条记录(甚至 1 M)运行整个文件验证脚本,它会永远运行。当我在 function 上调试时,下面的特定行导致了性能问题。

    #here using filter() , I am filtering out duplicates records alone from the duplicated list
    #then creating a tuple over it containg a pair of values in which first element is the duplicated record and second is the duplicated count

    duplicates=set((x,dup_list.count(x)) for x in filter(lambda rec : dup_list.count(rec)>1,dup_list))

输出重复文件如下所示:

    RECORD|DUPLICATE_COUNT
    68881,2014-07-19 00:00:00.0,2518,PENDING_PAYMENT|2
    68835,2014-05-02 00:00:00.0,764,COMPLETE|2
    68878,2014-07-08 00:00:00.0,6753,COMPLETE|2
    68834,2014-05-01 00:00:00.0,6938,COMPLETE|2

任何人都可以帮我修改逻辑或编写新逻辑,以便我一次可以处理数百万条记录。在我的项目中,文件高达 40M 或 50M。

标签: pythonpython-2.x

解决方案


您正在list.count循环使用。这是非常低效的。相反,进行一次传递以获取计数,然后再进行一次传递以过滤这些计数。线性时间与二次时间。因此,使用快速collections.Counter对象:

from collections import Counter
def dupFind(dup_list=(),output_path=""):

    counts = Counter(dup_list)
    duplicates = {(x, c) for x, c in counts.iteritems() if c > 1}
    ...

请注意,我将您的默认dup_list参数切换为空元组而不是空列表。如果您不了解它们的工作原理,可变的默认参数可能会导致错误。

上述解决方案确实需要辅助空间,但它应该非常快,acollections.Counter本质上是dict针对计数进行优化的。


推荐阅读