我有一个功能可以计算项目列表在rows
下方显示的频率:
def count(pair_list):
return float(sum([1 for row in rows if all(item in row.split() for item in pair_list)]))
if __name__ == "__main__":
pairs = [['apple', 'banana'], ['cookie', 'popsicle'], ['candy', 'cookie'], ...]
# grocery transaction data
rows = ['apple cookie banana popsicle wafer', 'almond milk eggs butter bread', 'bread almonds apple', 'cookie candy popsicle pop', ...]
res = [count(pair) for pair in pairs]
实际上,len(rows)
为10000
且18000
中有pairs
个元素,因此count()
中列表理解的计算成本与在主要功能是昂贵的。
我尝试了一些并行处理:
from multiprocessing.dummy import Pool as ThreadPool
import multiprocessing as mp
threadpool = ThreadPool(processes = mp.cpu_count())
res = threadpool.map(count, pairs)
这也不会很快。事实上,15分钟后,我就放弃了工作,因为它看起来并没有结束。两个问题:1)如何加快count()
中的实际搜索速度? 2)如何检查threadpool.map
进程的状态(即查看剩下多少对进行迭代)?
答案 0 :(得分:1)
1)计算的总体复杂性是巨大的,它来自不同的来源:
a)您在低级别的计算中拆分行,因此python必须为每次迭代创建新的行拆分。为避免这种情况,您可以预先计算行数。这样的事情将完成这项工作(“计数”功能的微小变化):
rows2 = [row.split() for row in rows]
b)您逐个比较列表项,即使您只需要检查另一个列表中是否存在单词。在这里我们可以更多地调整它(并在“count”函数中使用rows3而不是rows2):
rows3 = [set(row.split()) for row in rows]
def count(pair_list):
return float(sum([1 for row in rows3 if all(item in row for item in pair_list)]))
c)用行中的每个单词成对检查每个单词。对于原始版本,每次调用“count”函数时,计算需要2 * len(行)* len(行)次迭代,而它可能需要更少。对于选项b),在良好的情况下,它可以降至2 * len(行),但是每对可以进行一次设置查找,而不是2。 诀窍是为每一行准备所有可能的单词*单词组合,并检查该集合中是否存在相应的单词元组。 因此,在main函数中,您可以创建复杂的不可变搜索结构:
rows4 = [set((a, b) for a in row for b in row) for row in rows2]
现在“count”看起来会有所不同,需要使用tuple而不是list:
def count2(pair):
return float(len([1 for row in rows4 if(pair in row)]))
所以你称之为有点不同: res = [count2(元组(对))对成对]
请注意,搜索结构创建在时间和空间上每行采用len(row.split())^ 2,因此如果您的行可能很长,则不是最佳。毕竟,选项b)可以更好。
2)你可以预测“计数”的呼叫次数 - 它是len(对)。计算“计数”功能的调用并在其中进行调试打印,例如每1000次调用。