我的邮件是以下格式的文本:
Name Number1 Number2 Number3(ID)
somename1 1234.5678 273.4234 2783
somename2 2384.2 12.54
somename3 234.1 98234.2
可以看出,由于数据的性质,列中可能缺少一些数字。但是,Name
列将始终包含某些内容,因为它是唯一标识符。
我正在以str
形式获取此数据,并想弄清楚两件事:a)在数据中还给定其他字符串时,请弄清楚如何识别此列表格式数据(即,指示is_tabular = 1或否的指示符)。其他str
数据将是其他一些非表格数据点。
b)如何自动保存此文本(为str
格式)并以表格形式显示,尽管表中缺少数据点,也就是将其另存为Pandas DataFrame或NumPy数组或另一种有用的格式。问题是丢失的数据点与str
的每一列之间的空格数并不总是一致。
我已经想到了各种方法来尝试保存此表格格式,以及识别给定的str
是否为表格格式str。我考虑过预测方法,基于规则的方法(使用正则表达式),甚至OCR类型的方法,都可以将表格格式的字符串转换为Pandas DataFrame或其他方式。
但是,看来我的方法没有成功,我想知道是否有一种算法或方法可以用来将该字符串识别为表格(返回一个布尔值,说明该字符串是否为表格格式),以及是否有一种简便的方法将其转换为适当的数据格式,例如Pandas DataFrame,NumPy数组等,尽管缺少值。由于数据不是逗号分隔的(这只是某种格式的字符串,每行之间都有\n
字符),所以我想知道这些列是否被空格分隔(如上所示) ,但是每行上的空格宽度可能不一致,是否有一种简便的方法可以将此文本转换为DataFrame?
这样的启发式方法似乎行得通。它读取第一行以找出每个标头的起始偏移量,然后针对每一行使用该信息来找出每个数据最可能的列。
col_offset
参数是当列标题不左对齐时所需的错误软键。数据的开始。可以通过不仅查看列,而且查看适当的行来替换它,以查看要调整多少以推断出的列起始偏移量以适应此情况。
import re
# at least 3 consecutive non-whitespace is data; adjust if necessary
data_re = re.compile(r"[\S]{3,}")
def examine_headers(header_row):
column_headers = []
for i, match in enumerate(data_re.finditer(header_row)):
start = match.start(0)
if i == 0:
start = 0
column_headers.append((match.group(0), start))
return column_headers
def extract_row(column_headers, row, col_offset=0):
row_data = {}
for data_match in data_re.finditer(row):
try:
matching_header = [
header
for header in column_headers
if header[1] <= (data_match.start(0) + col_offset)
][-1]
except IndexError:
print(f"No matching header for datum {data_match} on row {row}")
else:
row_data[matching_header[0]] = data_match.group(0)
return row_data
# ------
rows = """
Name Number1 Number2 Number3(ID)
somename1 1234.5678 273.4234 2783
somename2 2384.2 12.54
somename3 234.1 98234.2
"""
rows = rows.strip("\n").splitlines()
column_headers = examine_headers(rows[0])
for row in rows[1:]:
print(extract_row(column_headers, row, col_offset=2))
输出为
{'Name': 'somename1', 'Number1': '1234.5678', 'Number2': '273.4234', 'Number3(ID)': '2783'}
{'Name': 'somename2', 'Number2': '2384.2', 'Number3(ID)': '12.54'}
{'Name': 'somename3', 'Number1': '234.1', 'Number3(ID)': '98234.2'}