使用Python从具有唯一标题打印技术的文件解析标题字符串(.inp扩展名)

时间:2018-05-17 20:02:02

标签: python pandas parsing header

我希望使用Python来解析文件中的数据帧(对于那些可能已经使用过的人,其SWMM模型输入/ inp文件)。文件头以相当独特的方式打印,这使得完全解析它变得非常困难。我试图从文件中读取的一个带有麻烦标题的数据帧示例是:

;;                                                 Param    
;;Node           Parameter        Time Series      Type     
;;-------------- ---------------- ---------------- -------- 
80408            FLOW             80408            FLOW     
81009            FLOW             81009            FLOW     
82309            FLOW             82309            FLOW     

标题不是由制表符或任何固定数量的空格分隔的。此外,对于某些标题,当它们的长度太大时,它们占据两条垂直线,而其他标题只使用一条线。宽度也不固定,有多个这样的数据帧,它们的宽度都不同。

我所能做的就是抓住最底线作为标题。

 with open(inp_fname, 'r') as f:
        for line in f:
               headers = re.split("\s{2,}", line.replace(';',"").strip())

2 个答案:

答案 0 :(得分:2)

由于您的数据不适合read_fwf中的插值,因此您可以自行扫描和解析标题。一旦计算出列名和宽度,就可以将它们传递给read_fwf,并在第一个实行上打开文件指针。标题和数据之间的虚线分隔符是列宽的一个很好的指示符,因此我用它来计算列宽。

import pandas as pd
import re

# write a test file...
open('test.txt', 'w').write("""\
;;                                                 Param    
;;Node           Parameter        Time Series      Type     
;;-------------- ---------------- ---------------- -------- 
80408            FLOW             80408            FLOW     
81009            FLOW             81009            FLOW     
82309            FLOW             82309            FLOW     """)


def make_dataframe(filename):
    with open('test.txt') as fp:
        # grab header
        headers = []
        for line in fp:
            if not line.startswith(';;-'):
                # header line, swap '  ' for ';;' to maintain len
                headers.append('  ' + line[2:-1])
            else:
                break
        else:
            print("ERROR: Header separator not found")
            return None

        # end of header, convert '----' separators to field lengths
        field_lens = [len(m)+1 for m in re.findall(r"\-+", '--' + line[2:-1])]

        # flatten multiline column names
        start = 0
        pd_header = []
        for f_len in field_lens:
            pd_header.append(' '.join(field.strip()
                for field in (h[start:start+f_len] for h in headers)
                if field.strip()))
            start += f_len

        # read fix length columns
        df = pd.read_fwf(fp, header=None, names=pd_header, widths=field_lens,
            index_col=False)
        return df

df = make_dataframe('test.txt')
print(df)

答案 1 :(得分:1)

与@ tdelaney的代码相同的想法,更简洁:

from itertools import takewhile
import re
import pandas as pd

def make_dataframe(filename):
    with open(filename) as fp:
        div = []
        headers = list(takewhile(
            lambda x: not x.startswith(';;-') or div.append(x), fp))
        colspecs = [m.span() for m in re.compile("-+").finditer(div[0])]
        headers = [
            re.sub(" +", " ", " ".join(hl[lo : hi] for hl in headers)).strip()
            for lo, hi in colspecs]
        d = pd.read_fwf(fp, header = None,
            colspecs = [(lo - 2, hi - 2) for lo, hi in colspecs])
    d.columns = headers
    return d

print(make_dataframe('test.txt'))