0

自 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']

因此,只要我们没有遇到不同的单词,就应该更正这些项目。

真正的问题是每个项目可以是字符串或元组,否则不会那么困难。我尝试使用“扁平化”功能并检查每个项目的项目,然后重建原始结构,但处理过于繁重(数据库可以增长到更多 50 000 个序列)。

有人知道 Python 中的一些魔术技巧可以帮助我当前的问题吗?

非常感谢 !

4

3 回答 3

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

祝你好运!

于 2014-02-16T17:35:56.877 回答
0

你应该定义一个这样的函数:

def capitalize(structure):
    return [structure[0].capitalize(), (structure[1][0].capitalize(), structure[1][0].capitalize()), structure[2].capitalize()]

然而,这是一个非常丑陋的解决方案。您可能应该考虑另一种以更聪明的方式表示数据的方式。

于 2013-07-05T10:11:18.310 回答
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"]

这是使用代码的示例会话:

>>> 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"永远不会被修改,因为它们每个都遵循不同的早期值。

如果您的数据库无法更改,这可能是最好的解决方案,但更简单的方法是简单地强制所有数据进入相同的规范化方案。您可能可以编写一些相当简单的代码来检查现有数据以使其保持一致,然后只需标准化您获得的每个新项目,而无需担心以前的条目是什么。

于 2013-07-05T10:54:38.620 回答