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.3 KiB
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 <br>) |
最终决策:
- 坚决不用视觉模型:数值准确性是底线。
- 放弃“预分裂”:不在提取阶段破坏表格的物理结构(Row/Span),避免引入元数据丢失风险。
- 采用“懒分裂”:在验证逻辑中,针对特定单元格内容进行 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 优势分析
- 兼容性强:完美支持您截图中的 颅内出血 | 85 | 90 | P=0.01 这种每行独立 P 值的场景。
- 鲁棒性:如果只有第一行有 P 值(合并单元格视觉效果),代码中的 Broadcast 逻辑也能兜底。
- 定位精准:报错信息可以包含 line_index,告诉前端是单元格里的第几行出错了。
4. 前端渲染规范 (Frontend Layer)
目标:使用最简单的 Web 技术还原 Word 样式,避免过度设计。
4.1 HTML 渲染策略
后端返回的 html 字段中,直接将 \n 替换为 <br>。
Python 端处理:
def generate_html_cell(text):
# 转义 HTML 特殊字符,并将换行转为 <br>
safe_text = html.escape(text)
return safe_text.replace("\n", "<br>")
前端展示效果:
<td>
277 (14.65)<br>
85 (4.49)<br>
94 (4.97)
</td>
4.2 错误高亮策略
由于我们不再拆分表格行(DOM 结构),高亮的最小单位是 Cell(单元格)。
- 交互设计:
- 当发现第 2 行子数据错误时,高亮整个单元格。
- Tooltip 提示:鼠标悬停时,显示具体错误信息:“第 2 行数据 P 值校验不通过”。
- 进阶优化(V2.1 可选):
- 如果确实需要高亮某一行,Python 生成 HTML 时可以用 <span> 包裹每一行: <span id="r3c2_L0">277 (14.65)</span><br><span id="r3c2_L1">85 (4.49)</span>
- 但 MVP 阶段建议只高亮单元格,性价比最高。
5. 总结
| 模块 | 核心动作 | 复杂度 |
|---|---|---|
| Python 提取 | 保持 \n,不拆行,输出标准 JSON | ⭐ (低) |
| Python 验证 | split('\n'),循环对齐,独立计算 | ⭐⭐ (中) |
| 前端渲染 | 使用 <br> 换行,CSS 控制对齐 | ⭐ (低) |
| 前端高亮 | 高亮整个单元格,Tooltip 说明行号 | ⭐ (低) |
这是目前最务实、风险最低的实施路径。 请开发团队以此为准。