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