从2天开始,我就陷入了一个看似简单的Python问题。 它涉及更新混合字符串/元组序列的“数据库”(总是具有相同的结构),但具有区分大小写的错误更正。
例如,数据库是:
[['Abc', ('Def', 'Ghi'), 'Jkl'],
['Abc', ('Def', 'Mno'), 'Pqr'],
['123', ('456', '789'), '012'],
['ToTo', ('TiTi', 'TaTa'), 'TeTe']]
现在,如果我输入具有相同单词但不同情况的另一个序列,我希望它能自动更正:
['abc', ('def', 'ghi'), 'jkl'] -> ['Abc', ('Def', 'Ghi'), 'Jkl']
['abc', ('def', 'XYZ'), 'jkl'] -> ['Abc', ('Def', 'XYZ'), 'jkl']
['abc', ('titi', 'tata'), 'tete'] -> ['Abc', ('titi', 'tata'), 'tete']
所以只要我们没有遇到不同的词,就应该纠正这些项目。
真正的问题是每个项目可以是字符串或元组,否则不会那么困难。 我尝试使用'flatten'函数并检查每个项目的项目,然后重建原始结构,但处理过重(数据库可以增长到更多的50 000个序列)。
有人知道Python中的一些神奇技巧会帮助我解决当前的问题吗?
非常感谢!
答案 0 :(得分:1)
使用Brown和PorterStemmer尝试NLTK。布朗有一个非常广泛的单词列表和一个预先学习的词干分析器。
示例:
from nltk import PorterStemmer
from nltk.corpus import brown
import sys
from collections import defaultdict
import operator
def sortby(nlist ,n, reverse=0):
nlist.sort(key=operator.itemgetter(n), reverse=reverse)
class mydict(dict):
def __missing__(self, key):
return 0
class DidYouMean:
def __init__(self):
self.stemmer = PorterStemmer()
def specialhash(self, s):
s = s.lower()
s = s.replace("z", "s")
s = s.replace("h", "")
for i in [chr(ord("a") + i) for i in range(26)]:
s = s.replace(i+i, i)
s = self.stemmer.stem(s)
return s
def test(self, token):
hashed = self.specialhash(token)
if hashed in self.learned:
words = self.learned[hashed].items()
sortby(words, 1, reverse=1)
if token in [i[0] for i in words]:
return 'This word seems OK'
else:
if len(words) == 1:
return 'Did you mean "%s" ?' % words[0][0]
else:
return 'Did you mean "%s" ? (or %s)' \
% (words[0][0], ", ".join(['"'+i[0]+'"' \
for i in words[1:]]))
return "I can't found similar word in my learned db"
def learn(self, listofsentences=[], n=2000):
self.learned = defaultdict(mydict)
if listofsentences == []:
listofsentences = brown.sents()
for i, sent in enumerate(listofsentences):
if i >= n: # Limit to the first nth sentences of the corpus
break
for word in sent:
self.learned[self.specialhash(word)][word.lower()] += 1
def demo():
d = DidYouMean()
d.learn()
# choice of words to be relevant related to the brown corpus
for i in "birdd, oklaoma, emphasise, bird, carot".split(", "):
print i, "-", d.test(i)
if __name__ == "__main__":
demo()
安装: http://www.nltk.org/install.html
您还需要这些数据才能使其正常工作: http://www.nltk.org/data.html
祝你好运!答案 1 :(得分:0)
你应该定义一个这样的函数:
def capitalize(structure):
return [structure[0].capitalize(), (structure[1][0].capitalize(), structure[1][0].capitalize()), structure[2].capitalize()]
然而,这是一个非常难看的解决方案。您应该以另一种方式思考以更聪明的方式表示数据。
答案 2 :(得分:0)
我建议使用字典将您的单词从一些通用形式(可能全部小写)转换为您想要在数据库中使用所有项目的“正确”形式。由于单词的路径很重要,我建议使用包含所有先前规范化路径的元组作为字典中的键:
_corrections = {}
def autocorrect(sequence):
normalized = () # used as keys into _autocorrection_dict
corrected = [] # values from _autocorrection_dict
for item in sequence:
if isinstance(item, str):
normalized += (item.lower(),)
corrected = _corrections.setdefault(normalized, corrected + [item])
elif isinstance(item, tuple):
sub_norm = tuple(subitem.lower() for subitem in item)
if normalized + (sub_norm,) not in _corrections:
sub_corrected = ()
for subitem in item:
sub_result = _corrections.setdefault(normalized + (subitem.lower(),),
corrected + [subitem])
sub_corrected += (sub_result[-1],)
_corrections[normalized + (sub_norm,)] = corrected + [sub_corrected]
normalized += (sub_norm,)
corrected = _corrections[normalized]
else:
raise TypeError("Unexpected item type: {}".format(type(item).__name__))
return corrected
此代码的第一部分(if
块)处理简单的字符串值。它应该很容易理解。它构建了一个“标准化”值的元组,这只是到目前为止所有小写的字符串。规范化的元组用作_corrections
字典的密钥,我们在其中存储“正确”的结果。神奇发生在setdefault
调用中,如果尚不存在,则创建一个新条目。
代码的第二部分(elif
块)是处理元组值的更复杂的部分。首先,我们规范化元组中的所有字符串,并检查我们是否已经有结果(如果我们已经看到这个确切的元组,这可以让我们避免其余的)。如果没有,我们必须检查元组中每个子项的先前已保存的结果(如果["foo", ("BAR", "BAZ")]
已经有"BAR"
和"BAZ"
,那么["foo", "bar"]
已经更正["foo", "baz"]
1}}和>>> autocorrect(['Abc', ('Def', 'Ghi'), 'Jkl'])
['Abc', ('Def', 'Ghi'), 'Jkl']
>>> autocorrect(['ABC', ("DEF", "GGGG"), "JKL"])
['Abc', ('Def', 'GGGG'), 'JKL']
>>> autocorrect(['abC', 'gggg', 'jkL'])
['Abc', 'GGGG', 'jkL']
)。一旦我们为元组中的每个子项找到了正确的值,我们就可以将它们全部放在一起,然后将组合结果添加到字典中。使短路部分工作有点尴尬,但我认为这是值得的。
以下是使用代码的示例会话:
"Abc"
"Def"
始终采用相同的形式,使用时"GGGG"
和"Jkl"
也是如此。然而,{{1}}的不同形式不会被修改,因为它们每个都遵循不同的早期值。
虽然如果您的数据库无法更改,这可能是最佳解决方案,但更简单的方法是简单地将所有数据强制转换为相同的规范化方案。您可以编写一些相当简单的代码来遍历现有数据以使其保持一致,然后将每个新项目标准化,而无需担心以前的条目。