Files
AIclinicalresearch/docs/03-业务模块/RVW-稿件审查系统/06-开发记录/RVW V2.0 表格提取疑难杂症专项解决方案.md
HaHafeng f9ed0c2528 feat(rvw): Complete V2.0 Week 3 - Statistical validation extension and UX improvements
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>
2026-02-18 18:26:16 +08:00

6.4 KiB
Raw Blame History

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 算法逻辑

  1. 扫描 (Scan):遍历表格的每一行。
  2. 检测 (Detect):检查该行每一列的 段落数量 (Paragraph Count)
    • 例如Col 1 有 4 个段落Col 2 有 4 个段落Col 3 只有 1 个段落(如 P 值)。
  3. 分裂 (Explode)
    • 取最大段落数 max_para (如 4)。
    • 如果 max_para > 1则将此行分裂为 4 个新行。
  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. 总结

  1. 别用视觉模型:准确率风险太大,得不偿失。
  2. 用代码“分裂”段落Word 的 cell.paragraphs 是您的救星。
  3. 对齐策略:通常临床表格中,如果一列有多行,另一列只有一行(如 P 值),那一行 P 值通常是对齐第一行或者居中的。在做**数据验证L1/L2**时,我们需要编写逻辑:“如果检测到分裂行,且 P 值列为空,自动向上寻找最近的一个 P 值作为本行的验证依据。”

实施建议:

请 Python 工程师立即测试上述 explode_word_table_rows 逻辑。这能解决您 90% 的“HTML 只有一行”的问题。