4. 不良事件报告数据库构建实战

信号检测的质量上限,往往在数据进入分析库的那一刻就已经被决定了。无论后续使用比例失衡分析(如PRR、ROR)、贝叶斯方法(如BCPNN、MGPS),还是时序关联方法,如果源数据中的药品名称、事件术语、报告版本、去重标识没有经过规范化处理,算法输出的“信号”很可能只是数据噪声。本节围绕一个可落地的ETL(Extract-Transform-Load)流程,说明如何把来自不同渠道的原始不良事件报告,转化为可用于信号检测的分析库,并讨论工具选型的关键判断依据。

一、先明确目标数据模型,再谈抽取

ETL的起点不是写脚本,而是确定目标库的结构。面向信号检测的分析库,通常需要围绕“报告—药品—事件—患者”四个核心实体建模,并保留可追溯的源标识。一个最小可用的关系模型可以包含:

  • 报告主表(case):报告ID、源系统、源报告号、首次接收日期、最新版本日期、报告类型(自发/文献/研究)、严重性、国家/地区、去重键。
  • 药品表(drug):报告ID、药品角色(可疑/合并/相互作用)、通用名、商品名、活性成分、ATC编码、剂型、给药途径、剂量、起止日期、适应证。
  • 事件表(event):报告ID、事件术语原文、MedDRA PT、MedDRA SOC、严重性、起病日期、转归、因果关系评价。
  • 患者表(patient):报告ID、年龄、年龄组、性别、体重、妊娠状态、相关病史。

其中“去重键”和“版本管理”是信号检测库区别于普通报表库的关键。同一病例可能因随访产生多个版本,若不去重,会人为放大某药品—事件组合的报告数,直接抬高PRR值。常见做法是以“源系统+源报告号”作为业务主键,保留最新版本,同时把历史版本写入审计表。

二、抽取:面对异构源头的现实处理

实际项目中,原始数据很少是整齐的CSV。常见来源包括:国家药品不良反应监测系统导出的XML/Excel、企业安全数据库(如Argus、ARISg)的定期导出、文献检索结果、以及临床试验的SAE清单。抽取阶段要解决三件事:

  1. 格式解析:XML按节点映射到字段;Excel需处理合并单元格与多行表头;PDF文献则通常需要人工或半自动录入,不建议直接作为自动化抽取对象。
  2. 增量识别:用“最新版本日期”或自增序列号作为水位线,只抽取上次ETL之后新增或更新的报告,避免全量重跑。
  3. 原始层留存:把抽取到的原始记录原样写入staging层(如raw_case、raw_drug),不做任何清洗。这样一旦下游转换规则出错,可以回溯重跑,而不必重新向源系统申请数据。
建议在staging层为每条记录附加三个技术字段:etl_batch_id、extract_time、source_file。它们不参与业务分析,但在排障和审计时非常关键。

三、转换:信号检测库最核心的清洗与规范化

转换阶段决定了数据能否被算法正确使用。以下按处理顺序说明关键操作,并给出示例参数。

1. 去重与版本合并。以业务主键分组,按版本日期降序取第一条作为当前版本。示例:同一报告号CN-2023-0451存在两个版本,版本日期分别为2023-06-01和2023-07-15,则保留后者,前者进入历史表。

2. 药品名称规范化。原始数据中“阿司匹林”“阿斯匹林”“Aspirin”“乙酰水杨酸”可能指向同一活性成分。做法是建立药品字典映射表,将通用名、商品名、别名统一映射到标准活性成分编码(如ATC或内部drug_id)。无法匹配的记录进入待审队列,由药学人员人工确认,而不是直接丢弃。

3. 事件术语编码。将事件原文映射到MedDRA PT。例如“皮疹”“皮肤红疹”“全身皮疹”可能都映射到PT“Rash”。这一步直接影响信号检测的粒度:映射过粗会掩盖真实信号,映射过细会稀释报告数。通常信号检测在PT层级进行,同时保留SOC层级用于汇总展示。

4. 日期与数值清洗。统一日期格式为ISO 8601;对缺失起病日期但存在接收日期的记录,可标记为“日期不完整”,在时序分析中单独处理。年龄统一换算为岁,婴儿按月龄换算并标注。

5. 派生字段计算。为信号检测准备常用派生列,例如:drug_event_pair(药品ID+事件PT)、report_year、is_serious、age_group。这些字段能显著减少后续查询的复杂度。

不良事件报告ETL流程与信号检测库构建 源数据层 监测系统 XML/Excel 企业安全库导出 文献 / SAE 清单 异构 · 多版本 · 增量 抽取 Staging 原始层 raw_case raw_drug raw_event 原样留存 · 可回溯 转换 转换层 · 清洗与规范化 去重 / 版本合并 药品名称映射 MedDRA 术语编码 日期 / 数值清洗 派生字段:drug_event_pair · report_year · is_serious · age_group 未匹配记录 → 待审队列(人工确认) 加载 信号检测分析库 case 报告主表 drug 药品表 event 事件表 patient 患者表 信号检测输入:药品—事件组合计数 · 报告数 · 严重性分布 PRR / ROR / BCPNN / MGPS 工具选型对照 轻量脚本方案 Python + pandas + SQL 适合:数据量小、快速验证 ETL平台方案 Airflow / Kettle / dbt 适合:多源、需调度与审计 选型判断依据 数据量级 · 更新频率 · 团队技能 · 审计追溯要求 · 与信号检测工具的衔接 无论选哪种,转换规则必须版本化、可重跑、可回溯

四、加载:让分析库可查、可复现

加载阶段建议采用“全量重建+增量追加”的混合策略。对于药品字典、MedDRA映射表等变化不频繁的维表,可以全量重建;对于报告事实表,按etl_batch_id增量追加,并保留批次日志。加载完成后,至少运行三类校验:

  • 完整性校验:报告数、药品记录数、事件记录数是否与staging层一致,差异率超过阈值(如0.5%)则告警。
  • 唯一性校验:业务主键是否唯一,是否存在重复报告。
  • 分布校验:Top 20药品和Top 20事件的报告数是否出现异常跳变,用于发现映射错误或源数据突变。

五、工具选型:没有最优,只有匹配

工具选型应围绕四个维度判断:数据量级、更新频率、团队技能、审计要求。若数据量在百万条以内、更新频率为月度、团队以统计和药学背景为主,Python + pandas + PostgreSQL 的组合足以支撑,开发周期短、调试直观。若数据来源超过五个、需要每日增量、且要求完整的任务依赖与失败重试,则Airflow或Kettle这类调度平台更合适。若企业已有数据仓库,dbt可以把转换逻辑SQL化、版本化,便于评审和复用。

需要强调的是,工具本身不解决数据质量问题。无论选择哪种方案,以下三条工程约束都应写入设计:转换规则必须版本化(每次变更记录规则ID和生效日期);ETL必须可重跑(同一批次重复执行结果一致);关键映射必须可追溯(每条标准化记录能回溯到原始值)。做到这三点,分析库才具备支撑信号检测的可信度。

常见误区:为了“先跑起来”而跳过药品名称和事件术语的规范化,直接用原始文本做分组统计。这样得到的PRR或ROR会把同一药品的不同写法拆成多个组合,导致真实信号被稀释,同时产生大量假阳性组合。规范化不是可选项,而是信号检测的前置条件。

六、一个可参考的最小ETL批次记录表

字段 示例值 用途
etl_batch_id 20240615_001 标识本次ETL批次
source_system NMPA_ADR 记录数据来源系统
extract_start / end 2024-06-01 / 2024-06-14 增量抽取水位线
raw_count / loaded_count 12,480 / 12,463 校验抽取与加载差异
unmatched_drug_count 37 进入待审队列的药品记录数
rule_version transform_v2.3 转换规则版本,支持回溯
status SUCCESS / WARN / FAIL 批次执行结果

这张表看似简单,却是ETL可运维的基础。当某天信号检测结果出现异常时,可以快速定位是源数据变化、映射规则变更,还是加载环节遗漏,而不是在多个脚本和临时文件中反复排查。数据工程的价值,最终体现在这种可定位、可复现的能力上。