自动更正单词数据库的大小写拼写

时间:2013-07-05 10:01:07

标签: python

从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中的一些神奇技巧会帮助我解决当前的问题吗?

非常感谢!

3 个答案:

答案 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}}的不同形式不会被修改,因为它们每个都遵循不同的早期值。

虽然如果您的数据库无法更改,这可能是最佳解决方案,但更简单的方法是简单地将所有数据强制转换为相同的规范化方案。您可以编写一些相当简单的代码来遍历现有数据以使其保持一致,然后将每个新项目标准化,而无需担心以前的条目。