鉴于文件如下:
1440927 1
1727557 3
1440927 2
9917156 4
第一个字段是in range(0, 200000000)
的ID。第二个字段表示类型,即in range(1, 5)
。类型1和类型2属于公共类别S1
,而类型3和类型4属于S2
。一个ID可能有几个不同类型的记录。该文件大小约为200MB。
问题是计算具有类型1或2的记录的ID的数量和数量 具有类型3或4的记录的ID。
我的代码:
def gen(path):
line_count = 0
for line in open(path):
tmp = line.split()
id = int(tmp[0])
yield id, int(tmp[1])
max_id = 200000000
S1 = bitarray.bitarray(max_id)
S2 = bitarray.bitarray(max_id)
for id, type in gen(path):
if type != 3 and type != 4:
S1[id] = True
else:
S2[id] = True
print S1.count(), S2.count()
虽然它给出了答案,但我认为它运行得有点慢。我该怎么做才能让它跑得更快?
修改
文件中有重复的记录。我只需要区分S1(类型1和类型2)和S2(类型3和类型4)。例如,1440927 1
和1440927 2
只计算一次,但不计算两次,因为它们属于S1。所以我必须存储ID。
答案 0 :(得分:3)
你在文件上使用迭代器,这意味着你当时只缓冲几行。每次缓冲区为空时,磁盘都需要查找,程序必须等待。
200MB很容易融入你的记忆中,所以获得所有线条会加快速度:
def gen(path):
# load all the lines,
lines = open(path).readlines()
split = (line.split() for line in lines)
return ((int(x), int(y)) for x,y in split)
答案 1 :(得分:2)
你是否与Python绑定?
egrep -e "[12]$" filename.txt | cut -d " " -f 1 | sort -u | wc -l
egrep -e "[34]$" filename.txt | cut -d " " -f 1 | sort -u | wc -l
这两个命令会计算filename.txt中每行末尾的(“1”或“2”)和(“3”或“4”)的出现次数,同时忽略重复的第一个字段。< / p>
可能比Python快......
答案 2 :(得分:2)
如果内存足够,您可以使用dict
代替bitarray.bitarray
。它可能会更快:
S1, S2 = {}, {} # dicts are slightly faster than `set()`
with open(path) as f:
for i, line in enumerate(f, 1):
id, sep, type = line.partition(" ")
if type == "1" or type == "2":
S1[id] = True
elif type == "3" or type == "4":
S2[id] = True
else:
print "WARNING: unknown type: %r in line %d: %r" % (type, i, line)
print len(S1), len(S2)
或者你可以先尝试排序:
def gettype(line):
return line[-1]
S1, S2 = 0, 0
with open(path) as f:
lines = f.read().splitlines()
lines.sort(key=gettype)
for type, group in itertools.groupby(lines, gettype):
ids = (line.partition(" ")[0] for line in group)
if type == "1" or type == "2":
S1 += len(set(ids))
elif type == "3" or type == "4":
S2 += len(set(ids))
else:
assert 0, (type, list(ids))
print S1, S2
第二种方法的渐近复杂性更差。
您可以使用line_profiler找出瓶颈所在。