# **RVW V2.0 表格提取疑难杂症专项解决方案 (v1.1 务实版)**
**问题焦点:** Word 表格“隐性多行”(单元格内多段落)导致的提取与验证错位 **核心策略:** **提取层保持原貌,验证层“懒分裂” (Lazy Split)** **技术栈:** Python (python-docx, pandas)
## **1\. 核心判断:技术选型定调**
| 维度 | 方案 A: 视觉模型 (VLM) | 方案 B: 结构重组 (预分裂) | 方案 C: 懒分裂 (推荐) |
| :---- | :---- | :---- | :---- |
| **原理** | 用 GPT-4V 截图识别 | 提取时把 Table 拆成 N 倍行 | **提取保持 \\n,验证时 split** |
| **准确性** | 低 (幻觉/小数点风险) | 中 (容易破坏合并单元格结构) | **高 (数据无损,逻辑灵活)** |
| **复杂度** | 高 (GPU/Prompt) | 高 (重构 DataFrame 结构) | **低 (仅在 Validator 中处理)** |
| **前端适配** | 难 (无法定位) | 难 (需定制虚拟行渲染) | **易 (原生 HTML \
)** |
**最终决策:**
1. **坚决不用视觉模型**:数值准确性是底线。
2. **放弃“预分裂”**:不在提取阶段破坏表格的物理结构(Row/Span),避免引入元数据丢失风险。
3. **采用“懒分裂”**:在验证逻辑中,针对特定单元格内容进行 split('\\n'),实现细粒度验证。
## **2\. 提取层规范 (Extractor Layer)**
**目标**:忠实还原 Word 文档的物理结构,不自作聪明地拆行。
### **2.1 Python 实现逻辑**
在 DocxTableExtractor 中,对于单元格内的多段落,直接使用换行符 \\n 连接。
def extract\_cell\_text(cell):
"""
提取单元格文本,保留段落结构
"""
\# 过滤掉完全空白的段落,保留有内容的段落
paragraphs \= \[p.text.strip() for p in cell.paragraphs if p.text.strip()\]
return "\\n".join(paragraphs)
**输出数据结构示例 (JSON)**:
{
"row\_index": 3,
"cells": \[
{ "text": "并发症\\n颅内出血\\n牙龈出血" }, // Col 0
{ "text": "277 (14.65)\\n85 (4.49)\\n94 (4.97)" }, // Col 1
{ "text": "χ²=5.687\\nχ²=0.003\\nχ²=13.745" }, // Col 3 (统计值)
{ "text": "0.017\\n0.01\\n\<0.001" } // Col 4 (P值)
\]
}
## **3\. 验证层规范 (Validator Layer)**
**核心逻辑:** 验证器在读取数据时,动态检测是否存在多行内容。如果存在,则在内存中“临时分裂”并逐一验证。
### **3.1 懒分裂验证算法 (Lazy Verification Logic)**
def verify\_row\_statistics(row\_data, col\_map):
"""
验证单行数据的统计逻辑(支持隐性多行)
"""
issues \= \[\]
\# 1\. 获取目标单元格的原始文本
\# 假设我们要验证 Col 1 (Group A) vs Col 2 (Group B) \-\> P Value
cell\_a\_text \= row\_data\[col\_map\['group\_a'\]\]
cell\_b\_text \= row\_data\[col\_map\['group\_b'\]\]
cell\_p\_text \= row\_data\[col\_map\['p\_value'\]\]
\# 2\. 懒分裂 (Lazy Split)
lines\_a \= cell\_a\_text.split('\\n')
lines\_b \= cell\_b\_text.split('\\n')
lines\_p \= cell\_p\_text.split('\\n')
\# 3\. 确定对齐基准(取最大行数)
max\_lines \= max(len(lines\_a), len(lines\_b), len(lines\_p))
\# 4\. 逐行验证 (Line-by-Line Validation)
for i in range(max\_lines):
\# 安全获取当前行的数据(处理长度不一致情况)
val\_a \= lines\_a\[i\] if i \< len(lines\_a) else ""
val\_b \= lines\_b\[i\] if i \< len(lines\_b) else ""
\# P 值匹配策略:
\# 如果 P 值列只有 1 行,但数据有 N 行 \-\> 广播机制 (Broadcast)
\# 如果 P 值列有 N 行 \-\> 一一对应 (One-to-One)
if len(lines\_p) \== 1 and max\_lines \> 1:
val\_p \= lines\_p\[0\] \# 策略 A: 共享 P 值
else:
val\_p \= lines\_p\[i\] if i \< len(lines\_p) else "" \# 策略 B: 独立 P 值
\# 跳过空行
if not val\_a or not val\_b or not val\_p:
continue
\# 执行具体的统计验证
\# 传入 line\_index=i 以便报错时定位
error \= validate\_single\_line(val\_a, val\_b, val\_p, line\_index=i)
if error:
issues.append(error)
return issues
### **3.2 优势分析**
1. **兼容性强**:完美支持您截图中的 颅内出血 | 85 | 90 | P=0.01 这种每行独立 P 值的场景。
2. **鲁棒性**:如果只有第一行有 P 值(合并单元格视觉效果),代码中的 Broadcast 逻辑也能兜底。
3. **定位精准**:报错信息可以包含 line\_index,告诉前端是单元格里的第几行出错了。
## **4\. 前端渲染规范 (Frontend Layer)**
**目标**:使用最简单的 Web 技术还原 Word 样式,避免过度设计。
### **4.1 HTML 渲染策略**
后端返回的 html 字段中,直接将 \\n 替换为 \
。
**Python 端处理:**
def generate\_html\_cell(text):
\# 转义 HTML 特殊字符,并将换行转为 \
safe\_text \= html.escape(text)
return safe\_text.replace("\\n", "\
")
**前端展示效果:**
\