1

我正在尝试使用 Python解析统计程序( Mplus )的输出。

输出的格式(此处的示例)以块、子块、列等形式构成,其中空格和中断非常重要。取决于例如。选项要求您在此处或那里获得额外的(子)块或列。

使用正则表达式来解决这个问题是一个 PITA 并且完全无法维护。我一直在研究解析器作为一个更强大的解决方案,但是

  1. 我对所有可能的工具和方法有点不知所措
  2. 给人的印象是它们不太适合这种输出。

例如,LEPL 有一种叫做line-aware parsing 的东西,它似乎朝着正确的方向发展(空格、块等),但仍然适用于解析编程语法,而不是输出。

建议朝哪个方向看将不胜感激。

4

5 回答 5

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

像这样的东西可能比正则表达式更好。

于 2010-08-20T18:59:22.710 回答
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))
于 2010-08-20T21:49:59.933 回答
1

根据您的示例,您拥有的是一堆不同的嵌套子格式,它们单独地很容易解析。令人不知所措的是格式的绝对数量以及它们可以以不同方式嵌套的事实。

在最低级别,您在一行上有一组空格分隔的值。这些线组合成块,块如何组合和相互嵌套是复杂的部分。这种类型的输出是为人类阅读而设计的,从来没有打算被“刮”回机器可读的形式。

首先,我会联系该软件的作者,并了解是否有可用的替代输出格式,例如 XML 或 CSV。如果正确完成(即不仅仅是用笨拙的 XML 包装的打印格式,或者用逗号替换空格),这将更容易处理。如果失败了,我会尝试提出格式的分层列表以及它们如何嵌套。例如,

  1. ESTIMATED SAMPLE STATISTICS开始一个块
  2. 在该块内MEANS/INTERCEPTS/THRESHOLDS开始一个嵌套块
  3. 接下来的两行是一组列标题
  4. 接下来是一行(或多行?)数据行,带有行标题和数据值

等等。如果您分别处理这些问题中的每一个,您会发现它很乏味但并不复杂。将上述每个步骤视为测试输入以查看它是否匹配以及是否匹配的模块,然后调用其他模块以进一步测试可能发生在块“内部”的事情,如果遇到不匹配的事情则回溯t 符合您的期望(顺便说一下,这称为“递归下降”)。

请注意,无论如何,您都必须执行类似的操作,以便构建可以操作的数据的内存版本(“数据模型”)。

于 2010-08-20T18:05:56.987 回答
0

你可以试试PyParsing。它使您能够为要解析的内容编写语法。除了解析编程语言之外,它还有其他示例。但我同意 Jim Garrison 的观点,即您的案例似乎不需要真正的解析器,因为编写语法会很麻烦。我会尝试一个蛮力解决方案,例如在空格处分割线。这不是万无一失的,但我们可以假设输出是正确的,所以如果一行有n 个标题,下一行将正好有n 个值。

于 2010-08-20T18:23:07.287 回答
0

事实证明,像这样的表格程序输出是我最早的 pyparsing 应用之一。不幸的是,那个确切的例子处理的是我无法发布的专有格式,但这里发布了一个类似的例子:http: //pyparsing.wikispaces.com/file/view/dictExample2.py

于 2010-08-21T00:01:25.773 回答