Извлечение элементов данных из больших неструктурированных текстовых файлов с помощью Python - PullRequest
0 голосов
/ 18 января 2019

Я пытаюсь извлечь элементы данных из больших неструктурированных текстовых файлов (от 1 000 000 до 15 000 000 строк на файл) без единого разделителя. Порядок элементов данных согласован.

Sample data:

  NAME    FIRSTNAME LASTNAME    DATE-OF-BIRTH        01/01/2019   ID-NUMBER  123     
  ADDRESS-1  1234 FAKE STREET                        COUNTY-CODE    123                             
  ADDRESS-2                                                                            
  CITY       NOWHERE                STATE   OH   ZIP  12345                            
RANDOM DATA .... 700+ LINES
  NAME  FIRSTNAME2 LASTNAME2    DATE-OF-BIRTH        01/01/2019   ID-NUMBER 4567    
  ADDRESS-1           123456 OTHER STREET            COUNTY-CODE  45678                                  
  ADDRESS-2                                                                            
  CITY      SOMEWHERE               STATE   MI   ZIP  65432                            
RANDOM DATA .... 700+ LINES

Я ищу способ создания вывода CSV со значениями нескольких полей, перечисленных ниже:

NAME, COUNTY-CODE, ZIP
FIRSTNAME LASTNAME, 123, 12345
FIRSTNAME2 LASTNAME2, 45678, 65432 

Данные НЕ разделены табуляцией и интервал будет различным. Любая помощь будет принята с благодарностью!

Ответы [ 2 ]

0 голосов
/ 18 января 2019

Хмм ...

Я предполагаю, что у вас есть несколько строк, каждая из которых содержит пары ID VALUE, и каждый кусок начинается с идентификатора NAME.

Так что я бы использовал модуль re для поиска ожидаемых шаблонов, когда NAME запускает новый элемент. Поскольку настоящие имена и фамилии могут использовать более одного слова (Джон Фицджеральд Кеннеди), я бы взял, чтобы ИМЯ было всем от ИМЕНИ до ДАТЫ РОЖДЕНИЯ.

ИМХО простой способ - создать dict при разборе строк и записать его, используя DictWriter, при достижении NAME и в конце файла. Я бы сохранил только первое вхождение каждого ключевого слова, если будет найдено больше одного, но вы также можете вызвать исключение.

Код может быть

import re
import csv

# prepare the patterns to search for
name = re.compile(r"NAME\s+(.*)\s+DATE")
zip_code = re.compile(r"ZIP\s*([0-9]+)")
county_code = re.compile(r"COUNTY-CODE\s*([0-9]+)")

with open("input.txt") as fdin, open("output.csv", newline='') as fdout:
    wr = csv.DictWriter(fdout, fieldnames=['NAME', 'COUNTY-CODE', 'ZIP'])
    elt = {}
    wr.writeheader()
    for line in fdin:
        # process NAME
        mx = name.search(line)
        if mx:
            if elt:                    # write previous dict if any
                wr.writerow(elt)
            elt = {'NAME': mx.group(1).strip()}   # initialize a new dict
        # process other keywords
        if not 'COUNTY-CODE' in elt:              # only keep first one
            mx =  county_code.search(line)
            if mx:
                elt['COUNTY-CODE'] = mx.group(1).strip()   # update the dict with it
        if not 'ZIP' in elt:
            mx = zip_code.search(line)
            if mx:
                elt['ZIP'] = mx.group(1)
    wr.writerow(elt)                                # don't forget last dict
0 голосов
/ 18 января 2019

Проблема очень похожа на найденную в другом вопросе SO .

Решение состоит в том, чтобы создать частичную грамматику, которая анализирует структуру распознанной конструкции, пропуская то, что не может быть распознано.

В вашем случае, используя textX , это было бы что-то вроде этого (я не проверял это, но вы получите изображение):

from textx import metamodel_from_str

mm = metamodel_from_str(r'''
    File: ( /(?s:.*?(?=NAME))/ persons*=Person | 'NAME' )*
          /(?s).*/;

    Person:
        'NAME' first_name=Name last_name=Name birth_date=Date
        'ADDRESS-1' address_1=UntilEOL
        'ADDRESS-2' address_2=UntilEOL
        'CITY' city=UntilEOL
    ;

    Name: /\w+/;
    Date: /\d{4}-\d{2}-\d{2}/;
    UntilEOL[noskipws]: /.*?\n/;
''')

data_model = mm.model_from_file('some_input_file.txt')

# Here data_model is an object with attribute `persons`
# where each person have attributes `first_name`, `last_name`, ...
# from the `Person` rule above.

Примечание. В этом решении предполагается, что в начале структурной части должно быть ключевое слово NAME, но ключевое слово можно найти в случайных данных, так как при неправильном разборе правила Person парсер будет использовать слово NAME и продолжить.

В зависимости от ваших реальных данных вам придется немного настроить грамматику (например, определенные регулярные выражения).

...