2010-08-20 2 views
1

Я пытаюсь проанализировать вывод статистической программы (Mplus) с использованием Python.Анализ выходных программ на основе базы данных с использованием Python

Формат вывода (example here) структурирован в блоках, подблоках, столбцах и т. Д., Где пробелы и разрывы очень важны. В зависимости от, например, запрашиваемые параметры, вы получаете дополнительный (дополнительный) блок или столбец здесь или там.

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

  1. Я немного overwhelmed всеми возможными инструментами и подходами;
  2. создается впечатление, что они не очень подходят для такого вывода.

E.g. LEPL имеет что-то по имени line-aware parsing, которое, кажется, идет в правильном направлении (пробелы, блоки, ...), но по-прежнему ориентировано на синтаксический анализ синтаксического анализа, а не на вывод.

Предложение, в каком направлении смотреть будет оценено.

ответ

1

Да, это боль, чтобы разобрать. У вас нет - однако - на самом деле требуется очень много регулярных выражений. Обычного split может быть достаточно для взлома этого документа в управляемые последовательности строк.

Это много того, что я называю блоками текста «Голова-Тело». У вас есть названия, строка «-», а затем данные.

Что вы хотите сделать, это свернуть структуру «голова-тело» в функцию генератора, которая дает отдельные словари.

def get_means_intecepts_thresholds(source_iter): 
    """Precondition: Current line is a "MEANS/INTERCEPTS/THRESHOLDS" line""" 
    head= source_iter.next().strip().split() 
    junk= source_iter.next().strip() 
    assert set(junk) == set([' ','-']) 
    for line in source_iter: 
     if len(line.strip()) == 0: continue 
     if line.strip() == "SLOPES": break 
     raw_data= line.strip().split() 
     data = dict(zip(head, map(float, raw_data[1:]))) 
     yield int(raw_data[0]), data 

def get_slopes(source_iter): 
    """Precondition: Current line is a "SLOPES" line""" 
    head= source_iter.next().strip().split() 
    junk= source_iter.next().strip() 
    assert set(junk) == set([' ','-']) 
    for line in source_iter: 
     if len(line.strip()) == 0: continue 
     if line.strip() == "SLOPES": break 
     raw_data= line.strip().split()) 
     data = dict(zip(head, map(float, raw_data[1:]))) 
     yield raw_data[0], data 

Цель состоит в том, чтобы потреблять голову и мусор с помощью одного набора операций.

Затем используйте строки данных, которые следуют с помощью другого набора операций.

Поскольку это генераторы, вы можете комбинировать их с другими операциями.

def get_estimated_sample_statistics(source_iter): 
    """Precondition: at the ESTIMATED SAMPLE STATISTICS line""" 
    for line in source_iter: 
     if len(line.strip()) == 0: continue 
    assert line.strip() == "MEANS/INTERCEPTS/THRESHOLDS" 
    for data in get_means_intercepts_thresholds(source_iter): 
     yield data 
    while True: 
     if len(line.strip()) == 0: continue 
     if line.strip() != "SLOPES": break 
     for data in get_slopes(source_iter): 
      yield data 

Что-то вроде этого может быть лучше, чем регулярные выражения.

1

Основываясь на вашем примере, у вас есть куча разных вложенных подформатов, которые по отдельности очень легко разбираются. Что может быть огромным, так это огромное количество форматов и тот факт, что они могут быть вложены разными способами.

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

Прежде всего, я хотел бы связаться с автором программного обеспечения и узнать, есть ли альтернативный формат вывода, например XML или CSV. Если все сделано правильно (т. Е. Не только формат печати, завернутый в неуклюжий XML, либо с запятой, заменяющей пробел), это будет намного проще в обращении. В противном случае я попытаюсь придумать иерархический список форматов и то, как они вложены. Например,

  1. ESTIMATED SAMPLE STATISTICS начинается блок
  2. внутри этого блока MEANS/INTERCEPTS/THRESHOLDS начинается вложенный блок
  3. Следующие две строки представляют собой набор заголовков столбцов
  4. За этим следует один (или больше?) строки данных, с заголовком строки и значениями данных

И так далее. Если вы подойдете к каждой из этих проблем отдельно, вы обнаружите, что это утомительно, но не сложно. Подумайте о каждом из вышеперечисленных шагов в качестве модулей, которые проверяют входные данные, чтобы увидеть, совпадает ли он, и если это произойдет, а затем вызовите другие модули, чтобы еще раз проверить, что может произойти «внутри» блока, возвращаясь назад, если вы доберетесь до того, t соответствует тому, что вы ожидаете (кстати, это называется «рекурсивный спуск»).

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

0

Вы можете использовать PyParsing. Это позволяет вам писать грамматику для того, что вы хотите проанализировать.У этого есть другие примеры, чем разбор языков программирования. Но я согласен с Джимом Гаррисоном в том, что ваше дело, похоже, не требует реального парсера, потому что писать грамматику будет громоздким. Я бы попробовал решение грубой силы, например. расщепляющие линии на белых пространствах. Это не безупречно, но мы можем предположить, что результат правильный, поэтому, если в строке есть заголовки n, следующая строка будет иметь ровно n значения.

1

Мое предложение состоит в том, чтобы сделать грубый массаж линий до более полезной формы. Ниже приведены некоторые эксперименты с вашими данными:

from __future__ import print_function 
from itertools import groupby 
import string 
counter = 0 

statslist = [ statsblocks.split('\n') 
      for statsblocks in open('mlab.txt').read().split('\n\n') 
      ] 
print(len(statslist), 'blocks') 

def blockcounter(line): 
    global counter 
    if not line[0]: 
     counter += 1 
    return counter 

blocklist = [ [block, list(stats)] for block, stats in groupby(statslist, blockcounter)] 

for blockno,block in enumerate(blocklist): 
    print(120 * '=') 
    for itemno,line in enumerate(block[1:][0]): 
     if len(line)<4 and any(line[-1].endswith(c) for c in string.letters) : 
      print('\n** DATA %i, HEADER (%r)**' % (blockno,line[-1])) 
     else: 
      print('\n** DATA %i, item %i, length %i **' % (blockno, itemno, len(line))) 
     for ind,subdata in enumerate(line): 
      if '___' in subdata: 
       print(' *** Numeric data starts: ***') 
      else: 
       if 6 < len(subdata)<16: 
        print('** TYPE: %s **' % subdata) 
       print('%3i : %s' %(ind, subdata)) 
0

Получается, что выход табличной программы, подобный этому, был одним из моих самых ранних применений пипарации. К сожалению, этот точный пример касался проприетарного формата, который я не могу опубликовать, но есть аналогичный пример, размещенный здесь: http://pyparsing.wikispaces.com/file/view/dictExample2.py.