3. EHR数据基础与工程视角

电子健康记录(EHR)是基因组数据融合工程中最主要的临床表型来源。要把基因型与临床表型对齐,第一步不是写代码,而是理解EHR里到底有哪些实体、它们如何被标识、如何被编码,以及这些编码在工程上会带来哪些约束。本节从数据模型、术语体系和工程约束三个层面展开。

一、EHR的核心数据模型:以患者为中心的实体关系

无论底层是关系型数据库、FHIR资源还是OMOP CDM,EHR的语义骨架基本一致:患者(Patient)是中心实体,围绕它展开就诊(Encounter),就诊之下挂载诊断(Condition/Diagnosis)、用药(Medication)、检验(Lab/Observation)和生命体征(Vital Signs)。理解这个层级关系,是后续做数据抽取、时间对齐和表型定义的基础。

关键点在于:患者标识通常不是单一字段。一个患者可能同时拥有院内病案号(MRN)、身份证号、医保号、以及跨机构的主索引(EMPI)ID。工程上必须明确哪一个是“主键”,哪些是“外部映射”,否则在跨系统关联时会出现一人多号或一号多人的问题。

就诊是临床事件的时间容器。一次住院、一次门诊、一次急诊都对应一个Encounter,带有入院时间、出院时间、科室、医生等属性。诊断、用药、检验都发生在某次就诊内,因此就诊ID是连接临床事件的时间锚点。

诊断记录疾病状态,通常用ICD编码表示;用药记录药物暴露,用RxNorm或ATC编码;检验记录实验室结果,用LOINC编码标识检验项目;生命体征如血压、体温、心率,通常也归入Observation类,但往往有独立的采集频率和单位约定。

EHR核心实体关系与术语编码映射 患者 Patient MRN / EMPI / 身份证 / 医保号 就诊 Encounter 入院/出院时间 · 科室 · 医生 诊断 Diagnosis ICD-10 / ICD-9 SNOMED CT 用药 Medication RxNorm / ATC 剂量 · 频次 · 途径 检验 Lab LOINC 结果值 · 单位 · 参考范围 生命体征 Vital LOINC / 自定义 血压 · 体温 · 心率 术语体系层:ICD(诊断) · LOINC(检验/体征) · RxNorm(药物) · SNOMED CT(临床发现) 工程约束:版本差异 · 一对多映射 · 本地扩展码 · 时间有效性

二、术语体系:ICD、LOINC、RxNorm的定位与差异

EHR中的临床概念几乎都以编码形式存储,而不是自由文本。三种最常用的术语体系各有明确分工:

  • ICD(国际疾病分类):用于诊断和手术操作编码。ICD-10-CM有约7万条诊断码,ICD-9-CM约1.4万条。工程上最需要注意的是版本切换——同一疾病在ICD-9和ICD-10中编码完全不同,跨版本队列必须做映射表。
  • LOINC(观测指标标识符逻辑命名与编码):用于检验项目和生命体征。LOINC码描述的是“测什么”,而不是“结果是什么”。例如“血肌酐”有独立的LOINC码,但结果值需要结合单位(mg/dL或μmol/L)才能解释。
  • RxNorm:用于临床药物。它提供从成分(Ingredient)到临床药品(Clinical Drug)的多层级粒度。工程上常见的问题是:处方记录可能只写了商品名,需要先归一化到RxNorm的Ingredient层,才能做跨机构的药物暴露分析。

三者的共同工程约束是:编码不是一对一的。一个本地检验项目可能映射到多个LOINC码,一个ICD-10码可能对应多个SNOMED CT概念。因此,术语映射必须保留映射来源、映射方向和置信度,不能简单做字符串替换。

三、工程视角下的关键约束

从工程实现角度看,EHR数据有四个必须提前处理的约束:

  1. 时间戳的粒度与一致性:就诊有入院/出院时间,检验有采样时间和报告时间,用药有开始/结束时间。基因组数据融合时,必须明确用哪个时间做对齐——是诊断时间、用药开始时间,还是检验采样时间。不同选择会直接改变表型定义的结果。
  2. 缺失值的语义:EHR中的空值可能表示“未测量”“未记录”“不适用”或“患者拒绝”。工程上不能统一当作NULL处理,否则会引入系统性偏差。
  3. 单位与参考范围:检验结果必须带单位。同一项目在不同实验室可能使用不同单位,LOINC码相同但单位不同时,需要做单位换算后再比较。
  4. 编码版本与本地扩展:医院常使用本地扩展码补充标准术语的不足。这些扩展码在跨机构交换时无法直接解释,必须附带映射说明。
工程建议:在构建基因组-临床融合数据集时,建议先建立一张“术语映射表”,至少包含:本地码、标准码、术语体系、版本、映射方向、置信度、生效时间。这张表是后续所有表型定义和队列筛选的基础设施。

四、一个可参考的抽取示例

假设我们要从EHR中抽取“2型糖尿病患者”的队列,并关联其用药和检验数据。一个合理的工程流程是:

-- 示例:基于ICD-10的诊断筛选(示意SQL,非实测)
SELECT p.patient_id, e.encounter_id, d.icd_code, d.diagnosis_time
FROM patient p
JOIN encounter e ON p.patient_id = e.patient_id
JOIN diagnosis d ON e.encounter_id = d.encounter_id
WHERE d.icd_code LIKE 'E11%'          -- ICD-10-CM 2型糖尿病
  AND d.diagnosis_time BETWEEN '2020-01-01' AND '2023-12-31';

-- 关联用药:RxNorm Ingredient层
SELECT m.patient_id, m.rxnorm_ingredient, m.start_date, m.end_date
FROM medication m
WHERE m.rxnorm_ingredient IN ('860975', '860974')  -- 二甲双胍相关
  AND m.start_date >= '2020-01-01';

-- 关联检验:LOINC码 + 单位
SELECT l.patient_id, l.loinc_code, l.result_value, l.unit, l.specimen_time
FROM lab l
WHERE l.loinc_code = '4548-4'          -- 糖化血红蛋白HbA1c
  AND l.unit = '%'
  AND l.specimen_time >= '2020-01-01';

这个示例的关键点不在于SQL本身,而在于每一步都显式处理了术语编码、时间范围和单位。实际工程中,还需要处理同一患者多次就诊的去重、同一检验多次结果的取值策略(首次、末次、均值或极值),以及用药记录的剂量归一化。

理解EHR的数据模型和术语体系,本质上是在为后续的基因组-临床关联建立“语义对齐”的基础。只有把患者标识、就诊时间、诊断编码、药物编码和检验编码都处理清楚,才能保证基因型与表型的关联分析不会因为数据层面的歧义而失效。