我正在创建一个字典如下:
y=[(1,2),(2,3),(1,2),(5,6)]
dict={}
for tup in y:
tup=tuple(sorted(tup))
if tup in dict.keys():
dict[tup]=dict[tup]+1
else:
dict[tup]=1
但是我的实际y
包含大约4千万个元组,有没有办法使用多处理来加速这个过程?
由于
答案 0 :(得分:3)
如果您想要忽略订单的计数,请使用带有计数器的frozenset
:
from collections import Counter
print(Counter(map(frozenset, y)))
使用其他答案中的tuples
:
In [9]: len(tuples)
Out[9]: 500000
In [10]: timeit Counter(map(frozenset, tuples))
1 loops, best of 3: 582 ms per loop
使用冻结集将意味着(1, 2)
和(2,1)
将被视为相同:
In [12]: y = [(1, 2), (2, 3), (1, 2), (5, 6),(2, 1),(6,5)]
In [13]: from collections import Counter
In [14]:
In [14]: print(Counter(map(frozenset, y)))
Counter({frozenset({1, 2}): 3, frozenset({5, 6}): 2, frozenset({2, 3}): 1})
如果使用多处理应用相同的逻辑,它显然会快得多,即使它没有击败使用多处理提供的内容。
答案 1 :(得分:2)
您可以遵循MapReduce方法。
from collections import Counter
from multiprocessing import Pool
NUM_PROCESSES = 8
y = [(1,2),(2,3),(1,2),(5,6)] * 10
## http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in xrange(0, len(l), n):
yield l[i:i+n]
## map
partial_counters = Pool(NUM_PROCESSES).map(Counter, chunks(y, NUM_PROCESSES))
## reduce
reduced_counter = reduce(Counter.__add__, partial_counters)
## Result is:
## Counter({(1, 2): 20, (5, 6): 10, (2, 3): 10})
这个想法是:
编辑:使用chunks(map(frozenset, y), NUM_PROCESSES)
来计算无序对。
答案 2 :(得分:0)
首先,不是在每次迭代中检查tup
中dict.keys
的成员资格,而是一个非常糟糕的想法,您可以使用collections.defaultdict()
来实现更加pythonic的目标:
from collections import defaultdict
test_dict = defaultdict(lambda:1)
for tup in y:
tup=tuple(sorted(tup))
test_dict[tup]=+1
其次,如果你想使用并发,你可能想要使用多线程或多处理,但是关于多线程,由于GIL多线程不能一次执行一个字节码,你无法遍历你的元组双方都喜欢BDS算法。
但是对于多处理,你会遇到另一个问题,即从每个核心访问一个共享内存并立即处理它以获取更多信息,请阅读此答案https://stackoverflow.com/a/30132673/2867928。
那么现在的诀窍是什么?
一种方法是将列表划分为小块,并使用多线程将您的函数应用于指定的部分。
另一种方法是使用coroutins和子程序,正如答案中提到的那样,Greg Ewing有一个great demonstration如何使用yield来使用协同程序来构建调度程序或多行为者模拟之类的东西。
答案 3 :(得分:0)
修改:编辑回答为线程安全的答案
multiprocessing
模块让它变得简单。
只需重构代码即可在函数中完成处理:
def process_tuple(tuples):
count_dict = {}
for tuple_ in tuples:
tuple_=tuple(sorted(tuple_))
if tuple_ in count_dict:
count_dict[tuple_] += 1
else:
count_dict[tuple_] = 1
return count_dict
将元组列表拆分为小组,然后使用map
处理所有组。
## http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in xrange(0, len(l), n):
yield l[i:i+n]
# cut tuples list into 5 chunks
tuples_groups = chunks(tuples, 5)
pool = Pool(5)
count_dict = {}
# processes chunks in parallel
results = pool.map(process_tuple, tuples_groups)
# collect results
for result in results:
count_dict.update(result)
multiprocessing.Pool
将处理线程之间的分配。
完整示例+基准:
import time
import random
start_time = time.time()
tuples = []
x,y = (100000, 10)
for i in range(x):
tuple_ = []
for j in range(y):
tuple_.append(random.randint(0, 9))
tuples.append(tuple(tuple_))
print("--- %s data generated in %s seconds ---" % (x*y, time.time() - start_time))
def process_tuple(tuples):
count_dict = {}
for tuple_ in tuples:
tuple_=tuple(sorted(tuple_))
if tuple_ in count_dict:
count_dict[tuple_] += 1
else:
count_dict[tuple_] = 1
return count_dict
from multiprocessing import Pool
start_time = time.time()
## http://stackoverflow.com/questions/312443/how-do-you-split-a-list-into-evenly-sized-chunks-in-python
def chunks(l, n):
"""Yield successive n-sized chunks from l."""
for i in xrange(0, len(l), n):
yield l[i:i+n]
# cut tuples list into 5 chunks
tuples_groups = chunks(tuples, 5)
pool = Pool(5)
count_dict = {}
# processes chunks in parallel
results = pool.map(process_tuple, tuples_groups)
# collect results
for result in results:
count_dict.update(result)
print("--- Multithread processed in %s seconds ---" % (time.time() - start_time))
start_time = time.time()
count_dict = {}
for tuple_ in tuples:
tuple_=tuple(sorted(tuple_))
if tuple_ in count_dict:
count_dict[tuple_] += 1
else:
count_dict[tuple_] = 1
print("--- Single thread processed in %s seconds ---" % (time.time() - start_time))
---在32.7803010941秒内生成10000000数据---
---多线程处理1.79116892815秒---
---单线程在2.65010404587秒处理---
答案 4 :(得分:0)
因为你想增加计数(不是简单地创建新的键/值对),所以字典不是线程安全的,除非你获得每个更新周围的信号量并在之后释放它 - 所以我不认为你将获得任何整体速度增益,实际上它可能会更慢。
如果您要对此进行线程化,那么每个线程最好更新自己的字典,然后在每个线程完成时合并结果,这样就毫无疑问是线程安全性。但是因为它可能是CPU限制的,所以你应该使用多处理而不是线程 - 多处理可以利用你所有的CPU核心。
另外,如果你使用collections.Counter,它会为你做计数,并支持合并,并且有一个有用的方法most_common(n)返回n个最高计数的键。