我有两个大文件。他们的内容如下:
134430513
125296589个
151963957个
125296589
该文件包含未排序的ID列表。某些ID可能会在单个文件中出现多次。
现在我想找到两个文件的交集部分。这就是ID出现在两个文件中。
我刚刚将这两个文件分为2组,s1
和s2
。并通过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,它将消耗更多内存。
答案 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
并再次读取文件,只考虑从0x200000000
到0x400000000
的整数(并在处理整数之前减去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)
您无需同时创建 s1
和s2
。首先读取第一个文件的行,将每行转换为整数(保存内存),将其放入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
当然,可以将所有内容组合到一个命令中,而无需创建中间文件。
<强>更新强>
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的交集,...