4. 临床数据管线的数据建模
临床研究数据管线与普通业务系统的最大差别,在于它必须同时表达“谁在什么时间点被观察了什么”以及“在观察之外又发生了什么”。前者是纵向访视数据,后者典型代表是不良事件(AE)。如果只把数据当成一张张表单堆进数据库,后续做时间窗校验、访视依从性统计、AE 与用药关联分析时就会处处掣肘。因此本节的核心任务,是把临床数据抽象为受试者、访视、表单、字段、编码五类实体,并用关系模型固定“结构”,用事件模型固定“时间与因果”。
五类实体与它们的关系骨架
受试者(Subject)是纵向数据的锚点,通常以研究号加受试者编号作为业务主键,例如 STUDY01-S-0012。访视(Visit)是时间轴上的观察节点,分为计划访视(如筛选期 SCR、基线 BL、第 4 周 V4、第 12 周 V12)与计划外访视(UNS)。表单(Form)是访视内的一次结构化采集单元,如生命体征、实验室检查、合并用药、AE 记录。字段(Field)是表单内的最小采集项,如收缩压、检验值、AE 起止日期。编码(Code)是字段取值的受控词表映射,如 MedDRA 的 PT/LLT、WHO Drug 的 ATC 编码、CDISC 的 CT 代码。
它们的关系可以概括为:一个受试者拥有多个访视,一个访视包含多张表单,一张表单包含多个字段,一个字段的取值可映射到一个或多个编码。这是一条典型的“一对多”链。真正需要工程化处理的是链上的两个约束:其一,访视与表单之间不是硬绑定,而是通过“访视计划(Visit Schedule)”定义某访视应采集哪些表单,从而支持不同队列、不同阶段的差异化采集;其二,字段与编码之间是多对多,因为同一检验值可能同时映射到标准术语与本地术语。
关系模型:把“结构”固定下来
关系模型解决的是“数据长什么样”。在物理表设计上,建议至少拆出以下核心表:受试者表(subject_id、study_id、site_id、入组日期、状态)、访视表(visit_id、subject_id、visit_code、计划日期、实际日期、访视状态)、表单表(form_id、visit_id、form_code、采集时间、录入人)、字段值表(field_id、form_id、field_code、value、unit、采集时间)、编码映射表(code_id、field_code、local_value、standard_system、standard_code、版本)。
这里有一个容易被忽略的设计点:字段值表不应把每个字段做成独立列,而应采用“长表”结构,即一行一个字段值。这样新增字段无需改表结构,代价是查询时需要按 form_id 做行转列。对于临床数据管线而言,长表更利于版本管理和稽查追踪,因为每次修改都可以作为新行追加,保留完整变更历史。
编码映射表需要特别处理版本问题。MedDRA 每年 3 月和 9 月各发布一次新版本,同一个 AE 术语在不同版本下可能对应不同的 PT 编码。因此映射表必须包含 standard_version 字段,并在管线中记录“编码时使用的版本”。否则当监管要求按特定版本重新编码时,历史数据将无法追溯。
事件模型:把“时间与因果”表达清楚
事件模型解决的是“数据在时间轴上如何发生”。纵向访视数据本质上是“计划时间点 + 实际时间点 + 窗口”的三元组。例如方案规定 V4 为 D28±3 天,受试者实际在 D31 完成访视,则窗口内合规;若在 D35 完成,则超出窗口,需要标记为方案偏离。事件模型要求访视表同时存储计划日期与实际日期,并计算窗口偏移量。
不良事件数据则更复杂,它不依附于某个访视,而是有独立的起止时间。AE 表至少需要:ae_id、subject_id、ae_term、开始日期、结束日期、严重程度、严重性标志(SAE)、与试验药物的因果关系、采取的措施、转归。其中开始日期和结束日期构成一个时间区间,这个区间可能与多个访视重叠。工程上通常用区间重叠判断来回答“某次访视时该 AE 是否仍在持续”。
下面给出一个可参考的 AE 与访视重叠判断的伪代码逻辑,用于管线中的衍生变量计算:
-- 判断 AE 是否与某次访视重叠
-- 访视窗口: visit_start = 实际访视日期 - 窗口下限
-- visit_end = 实际访视日期 + 窗口上限
-- AE 区间: ae_start, ae_end (若未结束则用数据截止日)
SELECT ae.ae_id, v.visit_code
FROM ae
JOIN visit v ON ae.subject_id = v.subject_id
WHERE ae.ae_start <= v.visit_end
AND COALESCE(ae.ae_end, '9999-12-31') >= v.visit_start;
这段逻辑在管线中通常被封装为“AE 与访视关联”衍生步骤,输出一张桥接表,供后续做“访视时 AE 状态”分析。注意区间判断使用闭区间还是半开区间需要全文统一,本课程约定为闭区间,即 AE 结束日当天仍视为持续。
纵向访视数据的结构化表达要点
纵向数据在建模时最容易出现的问题是“访视缺失”与“访视未做”混淆。前者表示该访视在计划中存在但受试者未参加,后者表示该访视因方案设计不要求该受试者执行。两者在统计中的处理方式完全不同。因此访视表需要增加 visit_status 字段,取值至少包括:已完成、未完成、未做、计划外。未完成还需记录原因,如受试者失访、撤回知情同意、研究者判断等。
另一个要点是访视与表单的“应填/实填”比对。管线应能自动生成一张访视依从性表,列出每个受试者每个计划访视应填表单数、实填表单数、缺失字段数。这张表是数据清理阶段最常用的工作底稿,也是锁库前必须核查的交付物之一。
不良事件数据的结构化表达要点
AE 数据的结构化难点在于“术语编码”与“因果关系”两个维度。术语编码要求把研究者填写的自由文本(如“头疼”“头痛”“headache”)统一映射到 MedDRA 的 PT 和 LLT。工程上通常先做本地同义词表,再调用编码接口或人工编码,最后回写标准编码与版本。因果关系则通常采用五级分类:肯定有关、很可能有关、可能有关、可能无关、无关。管线中应把因果关系作为受控字段,避免自由文本导致统计口径不一致。
此外,AE 与合并用药、AE 与实验室检查异常之间往往存在关联分析需求。建议在事件模型之上再建一层“事件关联表”,记录 AE 与相关用药、相关检查的关联关系及关联依据。这样当需要回答“某受试者肝功能异常是否与合并用药相关”时,可以直接沿关联表追溯,而不必每次重新做文本匹配。
把五类实体和两层模型落地后,临床数据管线就具备了支撑纵向访视分析与不良事件分析的基础结构。后续的清洗、衍生、统计编程都可以在这套结构上以可追溯、可复现的方式展开,而不是每次面对一堆散落的表单重新拼装。