
本文介绍一种基于正则匹配与上下文记忆的稳健方法,用于从 section_name 中提取结构化章节 ID(如 1.1、1.3.1),并将其填充至 section_id 列;对无编号条目(如 Synopsis)自动关联最近匹配的编号,空值保持不变。
本文介绍一种基于正则匹配与上下文记忆的稳健方法,用于从 `section_name` 中提取结构化章节 id(如 `1.1`、`1.3.1`),并将其填充至 `section_id` 列;对无编号条目(如 `synopsis`)自动关联最近匹配的编号,空值保持不变。
在处理结构化文档类数据(如测试用例、标准规范或 PDF 表格解析结果)时,常遇到 section_name 列混合了带编号标题(如 "1.2.Schema12")和纯内容标题(如 "Synopsis")的情况。目标是为每一行准确推断其所属的逻辑章节 ID,并填充至新列 section_id——这不仅是字符串切分问题,更涉及上下文语义映射。
核心思路分为两步:
- 正则识别编号前缀:使用 re.match(r'(\d+(?:\.\d+)*)(.*)', s) 精确捕获连续数字与点号组成的层级 ID(支持 1、1.1、1.3.1 等任意深度),剩余部分作为“语义标识符”存入映射字典;
- 模糊反向匹配:对不含编号的行(如 "Synopsis"),遍历已记录的语义标识符,检查当前名称是否为其子串(if name.strip() in known_semantic_key),从而实现语义归属。
以下为完整可运行代码(兼容空值、None 和空白字符串):
import pandas as pd
import numpy as np
import re
# 构造示例数据(含空值与 None)
data = {
'section_name': [
'1.Test Summary9',
'1.1.Synopsis9',
'1.2.Schema12',
'1.3.1.Test Period I - Screening13',
'1.3.2.Period II - obes-Treatment 15',
'Synopsis',
'Test Period I - Screening',
None,
''
]
}
df = pd.DataFrame(data)
def extract_section_id(row, memo_dict):
name = row['section_name']
# 跳过 None 和纯空白
if pd.isna(name) or str(name).strip() == '':
return '' # 或返回 np.nan,按需选择
s = str(name).strip()
# 步骤1:尝试匹配编号前缀(如 1.3.1)
match = re.match(r'^(\d+(?:\.\d+)*)\.(.*)$', s)
if match:
sec_id, semantic_part = match.groups()
# 记录该语义片段对应的 ID(用于后续匹配)
memo_dict[semantic_part] = sec_id
return sec_id
else:
# 步骤2:对无编号行,在历史语义片段中查找包含关系
for semantic_key, sec_id in memo_dict.items():
if s in semantic_key or semantic_key in s:
return sec_id
return '' # 未匹配时返回空字符串
# 初始化记忆字典
section_map = {}
df['section_id'] = df.apply(lambda x: extract_section_id(x, section_map), axis=1)
print(df)
输出结果:
section_name section_id 0 1.Test Summary9 1 1 1.1.Synopsis9 1.1 2 1.2.Schema12 1.2 3 1.3.1.Test Period I - Screening13 1.3.1 4 1.3.2.Period II - obes-Treatment 15 1.3.2 5 Synopsis 1.1 6 Test Period I - Screening 1.3.1 7 None 8
✅ 关键优势说明:
- 鲁棒性高:显式处理 None、空字符串、前后空格;
- 语义感知:通过 s in semantic_key 实现“Synopsis 属于 1.1.Synopsis9”的合理映射,而非简单前缀匹配;
- 状态可维护:memo_dict 在 apply 过程中持续累积已知模式,天然支持多层级文档流;
- 正则精准:^(\d+(?:\.\d+)*)\. 确保只匹配开头的合法编号 + 点号,避免误截 Schema12 中的 12。
⚠️ 注意事项:
- 若存在歧义语义(如 "Summary" 同时出现在 1.Test Summary9 和 2.Summary Notes),建议增强匹配逻辑(例如改为 semantic_key.startswith(s) 或引入编辑距离);
- 生产环境建议将函数封装为独立模块,并添加日志记录未匹配项以便人工校验;
- 对超大 DataFrame,可改用 itertuples() 提升性能,但需注意 memo_dict 仍需跨行共享。
该方法平衡了准确性、可读性与工程实用性,适用于各类嵌套编号文档的结构化解析任务。










