使用机器学习或NLP处理格式错误的文本数据

Ble*_*der 8 python parsing nlp machine-learning

我正在尝试从包含人员条目的几个大型文本文件中提取数据.但问题是,我无法控制数据的来源.

它通常采用以下格式:

LASTNAME,名字中间名(也许是昵称)为什么这个文本在这里2012年1月25日

姓氏姓氏2001一些我不关心的文字

姓氏,名字等等... 2012年1月25日......

目前,我使用的是巨大的,其分离所有的正则表达式kindaCamelcase的话,那有上涨到年底每月名和名称很多特殊情况下的所有单词.然后我使用更多正则表达式来提取名称和日期的许多组合.

这似乎不是最佳的.

是否有任何用于Python的机器学习库可以解析格式错误的数据?

我试过NLTK,但它无法处理我的脏数据.我现在正在修补Orange,我喜欢它的OOP风格,但我不确定我是否在浪费时间.

理想情况下,我想做这样的事情来训练解析器(具有许多输入/输出对):

training_data = (
  'LASTNAME, Firstname Middlename (Maybe a Nickname)FooBarJanuary 25, 2012',
   ['LASTNAME', 'Firstname', 'Middlename', 'Maybe a Nickname', 'January 25, 2012']
)
Run Code Online (Sandbox Code Playgroud)

这样的事情是可能的,还是我高估了机器学习?任何建议将不胜感激,因为我想更多地了解这个主题.

Ble*_*der 3

我最终实现了一系列有点复杂的详尽正则表达式,其中包含使用基于文本的“过滤器”的所有可能的用例,这些过滤器在加载解析器时被替换为适当的正则表达式。

如果有人对代码感兴趣,我会将其编辑到这个答案中。


这基本上就是我用过的。为了用我的“语言”构造正则表达式,我必须创建替换类:

class Replacer(object):
    def __call__(self, match):
        group = match.group(0)

        if group[1:].lower().endswith('_nm'):
            return '(?:' + Matcher(group).regex[1:]
        else:
            return '(?P<' + group[1:] + '>' + Matcher(group).regex[1:]
Run Code Online (Sandbox Code Playgroud)

然后,我创建了一个泛型Matcher类,它为给定模式名称的特定模式构造了一个正则表达式:

class Matcher(object):
    name_component =    r"([A-Z][A-Za-z|'|\-]+|[A-Z][a-z]{2,})"
    name_component_upper = r"([A-Z][A-Z|'|\-]+|[A-Z]{2,})"

    year = r'(1[89][0-9]{2}|20[0-9]{2})'
    year_upper = year

    age = r'([1-9][0-9]|1[01][0-9])'
    age_upper = age

    ordinal = r'([1-9][0-9]|1[01][0-9])\s*(?:th|rd|nd|st|TH|RD|ND|ST)'
    ordinal_upper = ordinal

    date = r'((?:{0})\.? [0-9]{{1,2}}(?:th|rd|nd|st|TH|RD|ND|ST)?,? \d{{2,4}}|[0-9]{{1,2}} (?:{0}),? \d{{2,4}}|[0-9]{{1,2}}[\-/\.][0-9]{{1,2}}[\-/\.][0-9]{{2,4}})'.format('|'.join(months + months_short) + '|' + '|'.join(months + months_short).upper())
    date_upper = date

    matchers = [
        'name_component',
        'year',
        'age',
        'ordinal',
        'date',
    ]

    def __init__(self, match=''):
        capitalized = '_upper' if match.isupper() else ''
        match = match.lower()[1:]

        if match.endswith('_instant'):
            match = match[:-8]

        if match in self.matchers:
            self.regex = getattr(self, match + capitalized)
        elif len(match) == 1:
        elif 'year' in match:
            self.regex = getattr(self, 'year')
        else:
            self.regex = getattr(self, 'name_component' + capitalized)
Run Code Online (Sandbox Code Playgroud)

最后,还有通用Pattern对象:

class Pattern(object):
    def __init__(self, text='', escape=None):
        self.text = text
        self.matchers = []

        escape = not self.text.startswith('!') if escape is None else False

        if escape:
            self.regex = re.sub(r'([\[\].?+\-()\^\\])', r'\\\1', self.text)
        else:
            self.regex = self.text[1:]

        self.size = len(re.findall(r'(\$[A-Za-z0-9\-_]+)', self.regex))

        self.regex = re.sub(r'(\$[A-Za-z0-9\-_]+)', Replacer(), self.regex)
        self.regex = re.sub(r'\s+', r'\\s+', self.regex)

    def search(self, text):
        return re.search(self.regex, text)

    def findall(self, text, max_depth=1.0):
        results = []
        length = float(len(text))

        for result in re.finditer(self.regex, text):
            if result.start() / length < max_depth:
                results.extend(result.groups())

        return results

    def match(self, text):
        result = map(lambda x: (x.groupdict(), x.start()), re.finditer(self.regex, text))

        if result:
            return result
        else:
            return []
Run Code Online (Sandbox Code Playgroud)

它变得相当复杂,但它有效。我不会发布所有源代码,但这应该可以让人们开始。最后,它转换了一个这样的文件:

$LASTNAME, $FirstName $I. said on $date
Run Code Online (Sandbox Code Playgroud)

进入带有命名捕获组的已编译正则表达式。