首页 > 解决方案 > 使用 Python 从具有独特标题打印技术(.inp 扩展名)的文件中解析标题字符串

问题描述

我希望使用 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())

标签: pythonpandasparsingheader

解决方案


由于您的数据不适合插值,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)

推荐阅读