Files
AIclinicalresearch/docs/03-业务模块/RVW-稿件审查系统/06-开发记录/务实版.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.3 KiB
Raw Blame History

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>)

最终决策:

  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 替换为 <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 说明行号 (低)

这是目前最务实、风险最低的实施路径。 请开发团队以此为准。