Week 3 Development Summary: - Implement negative sign normalization (6 Unicode variants) - Enhance T-test validation with smart sample size extraction - Enhance SE triangle and CI-P consistency validation with subrow support - Add precise sub-cell highlighting for P-values in multi-line cells - Add frontend issue type Chinese translations (6 new types) - Add file format tips for PDF/DOC uploads Technical improvements: - Add _clean_statistical_text() in extractor.py - Add _safe_float() wrapper in validator.py - Add ForensicsReport.tsx component - Update ISSUE_TYPE_LABELS translations Documentation: - Add 2026-02-18 development record - Update RVW module status (v5.1) - Update system status (v5.2) Status: Week 3 complete, ready for Week 4 testing Co-authored-by: Cursor <cursoragent@cursor.com>
6.4 KiB
RVW V2.0 表格提取疑难杂症专项解决方案
问题焦点: Word 表格“假行”现象(单元格内多段落)导致的提取错位
核心策略: 从“视觉模型”回归“DOM 深度解析”
技术栈: Python (python-docx)
1. 核心判断:为什么不建议全量上视觉模型?
您提到用视觉模型(Vision Model,如 GPT-4V, Qwen-VL)来识别,这听起来很诱人(所见即所得),但在数据侦探场景下有致命缺陷:
| 维度 | 视觉模型 (VLM/OCR) | 原生解析 (python-docx) | 结论 |
|---|---|---|---|
| 数值准确性 | 95%~99% (存在幻觉风险) | 100% (直接读取 XML) | ❌ 审计场景不能有 1% 的误差 |
| 小数点敏感度 | 可能漏读小数点 (0.05 -> 005) | 绝对精准 | ❌ P 值验证的核心 |
| 对齐能力 | 强 (能看懂视觉对齐) | 弱 (需算法辅助) | ✅ 视觉模型优势 |
| 成本/速度 | 高/慢 (需 GPU 推理) | 极低/极快 (CPU 解析) | ❌ 影响并发性能 |
决策:
“数据”必须信赖 XML(代码),“结构”可以用算法还原。 我们不需要视觉模型来看数字,我们只需要一段更聪明的 Python 代码来拆解段落。
2. 现象诊断:什么是“隐性多行”?
在您的截图中,Word 表格的一行(Row)内部,用户使用了 回车键 (Enter) 或 软回车 (Shift+Enter) 进行了换行。
python-docx 的默认行为:
cell.text 会把这些段落拼接成一个字符串,例如 "DNT时间段\n<45 min\n45~60 min"。前端 HTML 渲染时,如果没有处理 \n,或者对应列的行数不匹配,就会导致错位。
3. 解决方案:行分裂算法 (Row Explosion)
我们需要在提取阶段,检测这种情况,并将“逻辑上的一行”分裂成“视觉上的多行”。
3.1 算法逻辑
- 扫描 (Scan):遍历表格的每一行。
- 检测 (Detect):检查该行每一列的 段落数量 (Paragraph Count)。
- 例如:Col 1 有 4 个段落,Col 2 有 4 个段落,Col 3 只有 1 个段落(如 P 值)。
- 分裂 (Explode):
- 取最大段落数 max_para (如 4)。
- 如果 max_para > 1,则将此行分裂为 4 个新行。
- 填充 (Fill):
- 对于原本有多段落的列:按顺序填充到新行。
- 对于只有 1 个段落的列(如 P 值 0.001):
- 策略 A(重复):每行都填 0.001。
- 策略 B(首行/合并):只填第一行,后面留空(前端处理为合并单元格)。
3.2 代码实现 Demo
请让 Python 工程师在 DocxTableExtractor 中加入以下逻辑:
from docx import Document
import pandas as pd
def explode_word_table_rows(table):
"""
高级表格提取:处理单元格内的多段落(隐性多行)
"""
structured_data = []
for row in table.rows:
\# 1\. 获取该行每一列的段落内容列表
\# cells\_content 结构: \[ \['DNT时间段', '\<45min', ...\], \['1299', '881', ...\], \['X2=..'\] \]
cells\_content \= \[\]
for cell in row.cells:
\# 过滤掉空段落,获取真实文本行
paras \= \[p.text.strip() for p in cell.paragraphs if p.text.strip()\]
if not paras:
paras \= \[""\] \# 保持占位
cells\_content.append(paras)
\# 2\. 计算该行“分裂”的最大高度
max\_height \= max(len(c) for c in cells\_content)
\# 3\. 如果是标准单行,直接添加
if max\_height \<= 1:
flat\_row \= \[c\[0\] if c else "" for c in cells\_content\]
structured\_data.append(flat\_row)
continue
\# 4\. 执行分裂 (Row Explosion)
\# 针对每一层(visual\_row\_index),构建一行数据
for i in range(max\_height):
new\_row \= \[\]
for col\_idx, cell\_paras in enumerate(cells\_content):
\# 策略:如何填充?
if len(cell\_paras) \> 1:
\# 情况 A:该列有多行,按顺序取
\# 如果当前层级超过了该列的行数,填空(或填最后一行)
val \= cell\_paras\[i\] if i \< len(cell\_paras) else ""
else:
\# 情况 B:该列只有一行(通常是统计值 P值)
\# 只有第一行填值,模拟“合并单元格”的视觉效果
\# 或者:val \= cell\_paras\[0\] (全部重复填充) \-\> 方便后续计算
val \= cell\_paras\[0\] if i \== 0 else ""
new\_row.append(val)
structured\_data.append(new\_row)
return pd.DataFrame(structured\_data)
# 使用示例
# doc = Document("sample.docx")
# df = explode_word_table_rows(doc.tables[0])
# print(df)
4. 前端渲染的配合
为了让“数据侦探”的高亮定位准确,后端返回的数据结构必须包含分裂后的坐标映射。
推荐的数据结构升级:
{
"row_id": "r4_exploded_0", // 原始第4行,分裂后的第0子行
"is_virtual": true, // 标记这是分裂出来的行
"cells": [
{ "text": "<45 min", "source_cell": "R4C1", "paragraph_index": 1 },
{ "text": "881 (46.59)", "source_cell": "R4C2", "paragraph_index": 1 },
{ "text": "", "source_cell": "R4C3", "is_merged_placeholder": true } // P值列留空
]
}
前端展示逻辑:
- 当后端返回 is_merged_placeholder: true 时,前端渲染时不显示内容,或者通过 CSS 渲染为合并单元格的样式(即不画上边框)。
5. 总结
- 别用视觉模型:准确率风险太大,得不偿失。
- 用代码“分裂”段落:Word 的 cell.paragraphs 是您的救星。
- 对齐策略:通常临床表格中,如果一列有多行,另一列只有一行(如 P 值),那一行 P 值通常是对齐第一行或者居中的。在做**数据验证(L1/L2)**时,我们需要编写逻辑:“如果检测到分裂行,且 P 值列为空,自动向上寻找最近的一个 P 值作为本行的验证依据。”
实施建议:
请 Python 工程师立即测试上述 explode_word_table_rows 逻辑。这能解决您 90% 的“HTML 只有一行”的问题。