我遇到一个速度问题,搜索一个非常大的列表。我有一个包含很多错误和非常奇怪的文字的文件。我正在尝试使用difflib在我拥有的650,000个单词的字典文件中找到最接近的匹配项。下面这种方法效果很好,但非常慢,我想知道是否有更好的方法来解决这个问题。这是代码:
from difflib import SequenceMatcher
headWordList = [ #This is a list of 650,000 words]
openFile = open("sentences.txt","r")
for line in openFile:
sentenceList.append[line]
percentage = 0
count = 0
for y in sentenceList:
if y not in headwordList:
for x in headwordList:
m = SequenceMatcher(None, y.lower(), x)
if m.ratio() > percentage:
percentage = m.ratio()
word = x
if percentage > 0.86:
sentenceList[count] = word
count=count+1
感谢您的帮助,软件工程与我的强项并不相近。非常感谢。
答案 0 :(得分:4)
您应该将headwordList
更改为set
。
测试word in headwordList
会很慢。它必须对headwordList
中的每个单词进行字符串比较,一次一个单词。它需要时间与列表的长度成比例;如果你加倍列表的长度,你将使测试所需的时间加倍(平均)。
使用set
时,执行in
测试的时间总是相同;它不依赖于set
中的元素数量。这将是一个巨大的加速。
现在,整个循环可以简化:
for x in headwordList:
m = SequenceMatcher(None, y.lower(), x)
if m.ratio() > percentage:
percentage = m.ratio()
word = x
if percentage > 0.86:
sentenceList[count] = word
所有这一切都是找到headwordList
中具有最高比率的单词并保留它(但只有在比率超过0.86时才保留)。这是一种更快的方法。我要将名称headwordList
更改为headwords
,因为我希望您将其设为set
而不是list
。
def check_ratio(m):
return m.ratio()
y = y.lower() # do the .lower() call one time
m, word = max((SequenceMatcher(None, y, word), word) for word in headwords, key=check_ratio)
percentage = max(percentage, m.ratio()) # remember best ratio
if m.ratio() > 0.86:
setence_list.append(word)
这可能看起来有点棘手,但它是在Python中执行此操作的最快方法。我们将调用内置max()
函数来查找具有最高比率的SequenceMatcher
结果。首先,我们构建一个“生成器表达式”,尝试headwords
中的所有单词,在每个单词上调用SequenceMatcher()
。但是当我们完成时,我们也想知道这个词是什么。因此生成器表达式生成元组,其中元组中的第一个值是SequenceMatcher
结果,第二个值是单词。 max()
函数无法知道我们关心的是比率,所以我们必须告诉它;我们通过创建一个测试我们关心的函数,然后将该函数作为key=
参数传递来实现。现在max()
找到我们比率最高的值。 max()
使用生成器表达式生成的所有值并返回单个值,然后将其解压缩到变量m
和word
。
在Python中,最佳做法是使用sentence_list
而不是sentenceList
等变量名称。请参阅以下指南:http://www.python.org/dev/peps/pep-0008/
使用递增索引变量并将其分配到列表中的索引位置不是一种好习惯。相反,从空列表开始,并使用.append()
方法函数追加值。
此外,您可能会更好地构建单词及其比率字典。
请注意,您的原始代码似乎有一个错误:只要任何单词的百分比超过0.86,所有单词都会保存在sentenceList
中,无论它们的比例是多少。我上面写的代码只保存了单词自身比例足够高的单词。
编辑:这是为了回答有关需要用括号括起来的生成器表达式的问题。
每当我收到错误消息时,我通常会自己拆分生成器表达式并将其分配给变量。像这样:
def check_ratio(m):
return m.ratio()
y = y.lower() # do the .lower() call one time
genexp = ((SequenceMatcher(None, y, word), word) for word in headwords)
m, word = max(genexp, key=check_ratio)
percentage = max(percentage, m.ratio()) # remember best ratio
if m.ratio() > 0.86:
setence_list.append(word)
这就是我的建议。但是如果你不介意一条复杂的线条看起来更加繁忙,你可以简单地添加一对额外的括号,如错误信息所示,因此生成器表达式是完全括号的。像这样:
m, word = max(((SequenceMatcher(None, y, word), word) for word in headwords), key=check_ratio)
Python允许您在将表达式传递给函数时省略生成器表达式周围的显式括号,但前提是它只是该函数的唯一参数。由于我们也传递了一个key=
参数,我们需要一个完全括号的生成器表达式。
但是我认为如果你将genexp拆分出来就更容易阅读。
编辑:@Peter Wood指出文档建议重用SequenceMatcher
来提高速度。我没有时间对此进行测试,但我认为这是正确的方法。
令人高兴的是,代码变得更简单了!总是一个好兆头。
编辑:我刚刚测试了代码。这段代码对我有用;看看它是否适合你。from difflib import SequenceMatcher
headwords = [
# This is a list of 650,000 words
# Dummy list:
"happy",
"new",
"year",
]
def words_from_file(filename):
with open(filename, "rt") as f:
for line in f:
for word in line.split():
yield word
def _match(matcher, s):
matcher.set_seq2(s)
return (matcher.ratio(), s)
ratios = {}
best_ratio = 0
matcher = SequenceMatcher()
for word in words_from_file("sentences.txt"):
matcher.set_seq1(word.lower())
if word not in headwords:
ratio, word = max(_match(matcher, word.lower()) for word in headwords)
best_ratio = max(best_ratio, ratio) # remember best ratio
if ratio > 0.86:
ratios[word] = ratio
print(best_ratio)
print(ratios)
答案 1 :(得分:4)
可能会提供一些小帮助的两件事:
1)使用this SO answer中的方法最有效地读取大文件。
2)从
更改您的代码for x in headwordList:
m = SequenceMatcher(None, y.lower(), 1)
到
yLower = y.lower()
for x in headwordList:
m = SequenceMatcher(None, yLower, 1)
你将每个句子转换为650,000次。没必要。
答案 2 :(得分:3)
1)我会将headwordList
存储为一个集合,而不是列表,从而可以更快地访问,因为它是一个散列数据结构。
2)您已将sentenceList
定义为列表,然后尝试将其用作sentenceList[x] = y
的字典。我会专门为计数定义一个不同的结构。
3)构建sentenceList
,不需要这样做。
for line in file:
if line not in headwordList...
4)你永远不会对line
进行标记,这意味着你将整行保存在sentenceList中的换行符之前,看看它是否在单词列表中
答案 3 :(得分:0)
这是一个数据结构问题。您想要做的是将列表转换为具有更快元素查找速度的内容,例如二叉搜索树在这里工作得很好:时间复杂度仅为O (log n)
而不是列表中的O (n)
(相比之下非常快)。
这里有一个相当简单的解释:
http://interactivepython.org/runestone/static/pythonds/Trees/balanced.html
但是如果您不熟悉树概念,您可能希望在几个章节之前开始:
http://interactivepython.org/runestone/static/pythonds/Trees/trees.html