如何使用python有效地找到两个大文件的交集?

时间:2011-09-07 09:39:27

标签: python algorithm set

我有两个大文件。他们的内容如下:

  

134430513
  125296589个
  151963957个
  125296589

该文件包含未排序的ID列表。某些ID可能会在单个文件中出现多次。

现在我想找到两个文件的交集部分。这就是ID出现在两个文件中。

我刚刚将这两个文件分为2组,s1s2。并通过s1.intersection(s2)得到交叉点。但它消耗大量内存并且看起来很慢。

那么有更好的或pythonic方式吗?如果文件中包含的内容太多,无法读入内存有限的set,我该怎么办?

编辑:我使用生成器将文件读入2组:

def id_gen(path):
    for line in open(path):
        tmp = line.split()
        yield int(tmp[0])

c1 = id_gen(path)
s1 = set(c1)

所有ID都是数字。并且最大id可能是5000000000.如果使用bitarray,它将消耗更多内存。

6 个答案:

答案 0 :(得分:4)

set(open(file1)) & set(open(file2))

相当于使用intersection,是最恐怖的方式。你可以通过

加快速度
set(int(x) for x in open(file1)) & set(int(x) for x in open(file2))

从那以后你将存储和比较整数而不是字符串。这只适用于所有ID都是数字的情况。

如果它仍然不够快,你可以转向更为迫切的风格:

# heuristic: set smaller_file and larger_file by checking the file size
a = set(int(x) for x in open(smaller_file))
# note: we're storing strings in r
r = set(x for x in open(larger_file) if int(x) in a)

如果两个文件都保证不包含重复项,您还可以使用列表来加快速度:

a = set(int(x) for x in open(smaller_file))
r = [x for x in open(larger_file) if int(x) in a]

请务必测量各种解决方案,并检查您是否实际上没有等待磁盘或网络输入。

答案 1 :(得分:3)

因此,算法不一定与python绑定,而是如果你不能在内存中表示集合中的所有id那么通用。如果整数的范围有限,则方法是使用大bitarray。现在您读取第一个文件并将bitarray中的整数设置为存在。 现在您阅读第二个文件,并输出bitarray中也存在的所有数字。

如果即使这还不够,您也可以使用多次扫描来分割范围。即在第一遍中,您只考虑小于0x200000000(1GB bitarray)的整数。 然后重置bitarray并再次读取文件,只考虑从0x2000000000x400000000的整数(并在处理整数之前减去0x200000000)。

通过这种方式,您可以在合理的运行时间内处理大量数据。

单次扫描的样本将是:

import bitarray
r = bitarray.bitarray(5000000000)

for line in open(file1):
    r[int(line)] = True

for line in open(file2):
    if r[int(line)]:
        print line

答案 2 :(得分:3)

其他人已经表明了更为习惯的方式 Python,但如果数据的大小确实太大,你可以 然后使用系统实用程序来排序和消除重复项 使用File是一个返回一行的迭代器这一事实 一次,做一些事情:

import os
os.system('sort -u -n s1.num > s1.ns')
os.system('sort -u -n s2.num > s2.ns')
i1 = open('s1.ns', 'r')
i2 = open('s2.ns', 'r')
try:
    d1 = i1.next()
    d2 = i2.next()
    while True:
        if (d1 < d2):
            d1 = i1.next()
        elif (d2 < d1):
            d2 = i2.next()
        else:
            print d1,
            d1 = i1.next()
            d2 = i2.next()
except StopIteration:
    pass

这可以避免一次有多行(对于每个文件) 在内存中(系统排序应该比任何东西都快 Python可以做,因为它针对这一项任务进行了优化。)

答案 3 :(得分:1)

您无需同时创建 s1s2。首先读取第一个文件的行,将每行转换为整数(保存内存),将其放入s1。然后对于第二个文件中的每一行,将其转换为整数,并检查该值是否在s1中。

这样,你就可以节省存储字符串的内存,也可以节省两套。

答案 4 :(得分:1)

AFAIK没有用Python做到这一点的有效方法,特别是如果你正在处理大量数据。

我喜欢rumpel的解决方案。但请注意bitarray是C扩展名。

我会使用shell命令来处理这个问题。您可以预处理文件以节省时间和时间。空间:

sort -u file1 file1.sorted
sort -u file2 file2.sorted

然后您可以使用diff找出相似之处:

diff --changed-group-format='' --unchanged-group-format='%=' file1.sorted file2.sorted

当然,可以将所有内容组合到一个命令中,而无需创建中间文件。

<强>更新

根据Can的建议,comm是更恰当的命令:

sort -u file1 file1.sorted
sort -u file2 file2.sorted
comm -12 file1.sorted file2.sorted

答案 5 :(得分:1)

对于大于内存的数据,您可以将数据文件拆分为10个文件,其中包含相同的最低数字文件。

所以s1.txt中以0结尾的所有id都将保存在s1_0.txt中。

然后使用set()查找s1_0.txt和s2_0.txt,s1_1.txt和s2_1.txt的交集,...