Разбор неструктурированного текста в Python - PullRequest
4 голосов
/ 14 сентября 2009

Я хотел разобрать текстовый файл, который содержит неструктурированный текст. Мне нужно получить адрес, дату рождения, имя, пол и удостоверение личности.

. 55 MORILLO ZONE VIII,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
F
01/16/1952
ALOMO, TERESITA CABALLES
3412-00000-A1652TCA2
12    
. 22 FABRICANTE ST. ZONE
VIII LUISIANA LAGROS,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
M
10/14/1967
AMURAO, CALIXTO MANALO13

В приведенном выше примере первые 3 строки - это адрес, строка, в которой просто "F" - это пол, DOB - это строка после "F", имя после DOB, идентификатор после имени, и нет. 12 под идентификатором находится индекс / номер записи

Однако формат не соответствует. Во второй группе адрес составляет 4 строки вместо 3, а индекс / запись №. добавляется после имени (если у человека нет поля идентификатора).

Я хотел переписать текст в следующий формат:

name, ID, address, sex, DOB

Ответы [ 5 ]

15 голосов
/ 14 сентября 2009

Вот первый удар по решению для пирупинга ( код, легко копируемый с пасты для пирипаринга ). Пройдите через отдельные части, согласно чередующимся комментариям.

data = """\
. 55 MORILLO ZONE VIII,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
F
01/16/1952
ALOMO, TERESITA CABALLES
3412-00000-A1652TCA2
12
. 22 FABRICANTE ST. ZONE
VIII LUISIANA LAGROS,
BARANGAY ZONE VIII
(POB.), LUISIANA, LAGROS
M
10/14/1967
AMURAO, CALIXTO MANALO13
"""

from pyparsing import LineEnd, oneOf, Word, nums, Combine, restOfLine, \
    alphanums, Suppress, empty, originalTextFor, OneOrMore, alphas, \
    Group, ZeroOrMore

NL = LineEnd().suppress()
gender = oneOf("M F")
integer = Word(nums)
date = Combine(integer + '/' + integer + '/' + integer)

# define the simple line definitions
gender_line = gender("sex") + NL
dob_line = date("DOB") + NL
name_line = restOfLine("name") + NL
id_line = Word(alphanums+"-")("ID") + NL
recnum_line = integer("recnum") + NL

# define forms of address lines
first_addr_line = Suppress('.') + empty + restOfLine + NL
# a subsequent address line is any line that is not a gender definition
subsq_addr_line = ~(gender_line) + restOfLine + NL

# a line with a name and a recnum combined, if there is no ID
name_recnum_line = originalTextFor(OneOrMore(Word(alphas+',')))("name") + \
    integer("recnum") + NL

# defining the form of an overall record, either with or without an ID
record = Group((first_addr_line + ZeroOrMore(subsq_addr_line))("address") + 
    gender_line + 
    dob_line +
    ((name_line +
        id_line + 
        recnum_line) |
      name_recnum_line))

# parse data
records = OneOrMore(record).parseString(data)

# output the desired results (note that address is actually a list of lines)
for rec in records:
    if rec.ID:
        print "%(name)s, %(ID)s, %(address)s, %(sex)s, %(DOB)s" % rec
    else:
        print "%(name)s, , %(address)s, %(sex)s, %(DOB)s" % rec
print

# how to access the individual fields of the parsed record
for rec in records:
    print rec.dump()
    print rec.name, 'is', rec.sex
    print

Печать:

ALOMO, TERESITA CABALLES, 3412-00000-A1652TCA2, ['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS'], F, 01/16/1952
AMURAO, CALIXTO MANALO, , ['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS'], M, 10/14/1967

['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS', 'F', '01/16/1952', 'ALOMO, TERESITA CABALLES', '3412-00000-A1652TCA2', '12']
- DOB: 01/16/1952
- ID: 3412-00000-A1652TCA2
- address: ['55 MORILLO ZONE VIII,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS']
- name: ALOMO, TERESITA CABALLES
- recnum: 12
- sex: F
ALOMO, TERESITA CABALLES is F

['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS', 'M', '10/14/1967', 'AMURAO, CALIXTO MANALO', '13']
- DOB: 10/14/1967
- address: ['22 FABRICANTE ST. ZONE', 'VIII LUISIANA LAGROS,', 'BARANGAY ZONE VIII', '(POB.), LUISIANA, LAGROS']
- name: AMURAO, CALIXTO MANALO
- recnum: 13
- sex: M
AMURAO, CALIXTO MANALO is M
4 голосов
/ 14 сентября 2009

Вы должны использовать любую регулярность и структуру текста.

Я предлагаю вам прочитать по одной строке за раз и сопоставить ее с регулярным выражением, чтобы определить его тип, заполнить соответствующее поле в объекте person. выписывать этот объект и начинать новый всякий раз, когда вы получаете поле, которое вы уже заполнили.

3 голосов
/ 14 сентября 2009

Это может быть излишним, но передовые алгоритмы машинного обучения для этого типа проблемы основаны на условных случайных полях . Например, Точное извлечение информации из научных статей используя условные случайные поля .

Существует программное обеспечение, которое делает обучение этих моделей относительно простым. См. Маллет или CRF ++ .

2 голосов
/ 14 сентября 2009

Вы, вероятно, можете сделать это с помощью регулярных выражений без особых затруднений. Если вы никогда не использовали их ранее, ознакомьтесь с документацией по python, а затем запустите redemo.py (на моем компьютере он находится в каталоге c: \ python26 \ Tools \ scripts).

Первая задача - разбить плоский файл на список объектов (один фрагмент текста на запись). Из фрагмента текста, который вы дали, вы можете разделить файл с шаблоном, совпадающим с началом строки, где первый символ - точка:

import re
re_entity_splitter = re.compile(r'^\.')

entities = re_entity_splitter.split(open(textfile).read())

Обратите внимание, что точка должна быть экранирована (по умолчанию это подстановочный знак). Обратите также внимание на букву перед шаблоном. Символ r обозначает формат «сырой строки», который освобождает вас от необходимости избегать escape-символов, что приводит к так называемой «чуме с обратной косой чертой».

После того, как файл разбит на отдельных людей, выбор пола и даты рождения совсем несложно. Используйте это:

re_gender     = re.compile(r'^[MF]')
re_birth_Date = re.compile(r'\d\d/\d\d/\d\d')

И вы уходите. Вы можете вставить плоский файл в демонстрационный графический интерфейс и поэкспериментировать с созданием шаблонов, соответствующих вашим потребностям. Вы получите его в кратчайшие сроки. Как только вы это освоите, вы можете использовать символические имена групп (см. Документы), чтобы быстро и безошибочно выбирать отдельные элементы.

1 голос
/ 14 сентября 2009

Вот быстрая взломанная работа.

f = open('data.txt')

def process(file):
    address = ""

    for line in file:
        if line == '': raise StopIteration
        line = line.rstrip() # to ignore \n
        if line in ('M','F'):
            sex = line
            break
        else:
            address += line

    DOB = file.readline().rstrip() # to ignore \n
    name = file.readline().rstrip()

    if name[-1].isdigit():
        name = re.match(r'^([^\d]+)\d+', name).group(1)
        ID = None
    else:
        ID = file.readline().rstrip()
        file.readline() # ignore the record #

    print (name, ID, address, sex, DOB)

while True:
    process(f)
...