我在使用Python编写的查询时遇到了一些问题(必须将它用于TensorFlow),由于输入数据集非常大,因此效果很好但速度太慢。查询完成可能需要5分钟以上,检查任务管理器我可以确认它确实在单核上运行。
以下是代码:
# Assume words is a list of strings
for i, pair in enumerate(sorted(
((word, words.count(word)) # Map each word to itself and its count
for word in set(words)), # Set of unique words (remove duplicates)
key=lambda p: p[1], # Order by the frequency of each word
reverse=True)): # Descending order - less frequent words last
# Do stuff with each sorted pair
我在这里做的只是获取输入列表words
,删除重复项,然后根据输入文本中的频率按降序对单词进行排序。
如果我使用PLINQ在C#中写这个,我会做这样的事情:
var query = words.AsParallel().Distinct()
.OrderByDescending(w => words.Count(s => s.Equals(w)))
.Select((w, i) => (w, i));
我找不到使用可能的内置库重写Python中的并行实现的简单方法。我看到了一些关于Pool扩展名的指南,但看起来它只是并行Select
操作的等价物,所以我仍然想念如何实现Distinct
和{{1 Python中的操作,并行。
是否可以使用内置库执行此操作,或者是否有常用的第三方库来执行此操作?
谢谢!
答案 0 :(得分:0)
您当前方法的问题主要基于words.count(word)
循环内的for
。这意味着您为set(words)
中的每个唯一单词迭代整个列表,并且只计算一个单词...相反,您可以使用Counter
并对列表进行单次传递。 Counter对象是一个字典,您可以将其用于排序中的键,频率查找为O(1)。即使在我的例子中有1000个“单词”,加速也是戏剧性的...对于更长的输入,我感到无聊等待timeit
完成:)
import string
from collections import Counter
import numpy as np # Just to create fake data
# Create some fake data
letters = list(string.ascii_lowercase)
new_words = [''.join(list(np.random.choice(letters, 3, replace=True)))
for x in range(1000)]
def original(search_list):
""" Your current approach """
for i, pair in enumerate(sorted(
((word, search_list.count(word))
for word in set(search_list)),
key=lambda p: p[1],
reverse=True)):
pass
def new_approach(search_list):
freq = Counter(search_list)
search_list = sorted(search_list, key=lambda x: freq[x], reverse=True)
new_list = []
checked = set()
for item in search_list:
if item not in checked:
new_list.append(item)
checked.add(item)
获取1000个“单词”的列表:
%timeit original(new_words)
26.6 ms ± 289 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
%timeit new_approach(new_words)
833 µs ± 30 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
在尝试使用multiprocessing
之类的内容之前,您应该先了解这种新方法是否适合您的需求,因为这可能会增加额外的代码复杂性,而这在修复时间复杂度问题后是不必要的。
编辑:
正如OP所指出的,我们可以跳过中间列表并通过简单地对Counter对象进行排序来设置:
def new_approach(search_list):
freq = Counter(search_list)
search_list = enumerate(sorted(freq, key=lambda x: freq[x], reverse=True))
新时间:
%timeit new_approach(new_words)
438 µs ± 6.31 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)