@YuchenXie和@PaulMcGuire建议的微优化可能不是你的主要问题,那就是你循环超过20000×20000=400000000对条目,然后有一个2000×2000用户对的内部循环。那会很慢的。在
幸运的是,通过预缓存i['users']中的用户id的set,并用一个简单的集合交集替换内部循环,可以更快地完成内部循环。这将Python解释器中的O(num_users^2)操作更改为C中的O(num_users)操作,这应该会有所帮助。(我只是用大小为2000的整数列表来计时;在我的电脑上,它从原来的156ms变为现在的41µs,速度提高了4000倍。)
您还可以通过注意到关系是对称的,来切断对位置的主循环的一半工作,因此没有必要同时执行i = a[1],q = a[2]和{},q = a[1]。在
考虑到这些和@PaulMcGuire的建议,再加上一些其他风格上的变化,您的代码将变成(注意:前面未测试的代码):from itertools import combinations, izip
data = db.collection.find()
a = list(data)
user_ids = [{user['id'] for user in i['users']} if 'users' in i else set()
for i in a]
with open("edges.csv", "wb") as f:
edges = csv.writer(f)
for (i, i_ids), (q, q_ids) in combinations(izip(a, user_ids), 2):
weight = len(i_ids & q_ids)
if weight > 0:
edges.writerow([i['id'], q['id'], weight])
edges.writerow([q['id'], i['id'], weight])
with open("nodes.csv", "wb") as f:
nodes = csv.writer(f)
for i in a:
nodes.writerow([
i['id'],
i['name'],
i['latitude'],
i['longitude'],
len(i['users']),
sum(len(p['photos']) for p in i['users']), # total number of photos
])
希望这足够加速了。如果不是,那么@YuchenXie的建议可能会有帮助,尽管我对此表示怀疑,因为stdlib/OS在缓冲此类问题上相当出色。(您可以使用文件对象上的缓冲设置。)
否则,它可能会归结为尝试从Python(Cython或手写C)中获取核心循环,或者给PyPy一次机会。不过,我很怀疑这会给你带来巨大的加速。在
你也可以把计算重量的方法推到Mongo中,这可能更聪明;我从来没有真正使用过它,所以我不知道。在