Python 3:自动完成算法

时间:2017-09-28 14:48:28

标签: python performance list autocomplete match

我在Python 3.6中编写了一些代码来自动完成用户输入的(部分)单词。我的目标是尽可能快速/高效地运行自动完成部分,作为一种开发我的编程技巧和更好地理解Python的方法。我仅限于使用标准库,因为我想探索使用这样的constaint可以实现的目标。

代码的第一部分是一个函数def load_wordfile,它加载一个.txt并创建一个单词列表,在整个过程中小写和清理单词。我试图通过列表理解和string.punctuation加快速度,尽管这不是我的主要关注点。

import string
from collections import Counter

def load_wordfile(filename):

    with open(filename, encoding='utf8') as f:

        # List comprehension and string.punctuation for efficiency and speed improvements
        return [word.strip(string.punctuation) for line in f for word in line.lower().split()]

然后在实例化期间将单词列表传递给类AutoCompleter。在这个类中,方法complete实际上执行自动完成。我希望自动完成的结果作为元组列表,其中元组中的第一个值是匹配的单词,第二个值是单词出现的相对频率(在.txt中)。返回的元组列表应按频率降序排列。我知道Counter对此有好处,所以我已经使用它了。

class AutoCompleter:

    def __init__(self, word_list):

        # Counter takes care of word counts and descending order
        self.counts = Counter(word_list)

        # Total word count for relative frequency calculation
        total = float(len(word_list))

        # Change the counts to frequencies
        for k, v in self.counts.items():
            self.counts[k] = v/total

        # Turn Counter dict into a list in descending order
        self.counts = self.counts.most_common()

    def complete(self, pattern):

        # Clean user entered word
        pattern = pattern.lower().strip(string.punctuation)

        # This is the potential bottleneck
        completions = [pair for pair in self.counts if pair[0].startswith(pattern)]

        return completions

在执行速度方面,我设法改进了我的第一次尝试,这导致了您在上面看到的代码。

但是,我相信complete仍然可以改进。为了证明我现在的位置,我在NLTK英语单词语料库上对complete进行了基准测试,发现这是一个很好的“上限”,需要搜索匹配的列表大小。

from nltk.corpus import words

# words.words() is a list of over 250,000 unique English words
cmp = AutoCompleter(words.words())
pattern = 'do'

%timeit -n 100 cmp.complete(pattern)

来自timeit的结果是:

41.6 ms ± 898 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

通过广泛的研究,我认为我可以使用某种二分搜索或bisect来加速搜索与用户输入相匹配的(部分)单词。或者可能以不同的方式将输入结构化为complete,找到匹配单词的索引,然后从不同的列表中拉出频率,最后将两者一起输出。

我也有一个想法,那就是用户键入“a”然后我应该只搜索以“a”开头的单词而不是传递列表中的每个单词。但是现在我对如何最好地实现这一点感到困惑。我也看过Stack Overflow,这有助于其他方面,但我还没有完全把所有的部分组合在一起,以便最终提升性能。

非常感谢您的帮助和建议。谢谢!

0 个答案:

没有答案