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

137 lines
6.4 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# **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 只有一行”的问题。