ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于FHIR与疾病本体的医疗数据互操作与语义映射实践

基于FHIR与疾病本体的医疗数据互操作与语义映射实践 简介面向医疗信息化研究者、系统架构师及卫生信息平台建设者这份文档以HL7 FHIR框架为核心针对中国医疗领域数据模型不统一、术语编码各异导致的信息交换难题提出融合疾病本体DO与本体映射技术的解决方案。文章首先梳理了FHIR标准的发展历程及国内外应用现状随后重点阐述了如何通过本体映射和迁移规则将异构系统中的疾病术语转化为FHIR标准化格式实现数据语义的统一与机器可理解。资源为单篇docx格式压缩包内共1个文件大小1.12MB当前已有64人学习。对关注医疗信息标准落地、医院信息系统互操作及电子健康档案共享的读者而言这份研究可帮助理解FHIR与本体结合的具体路径并为国内区域卫生信息平台的互联互通建设提供方法论参考。1. 为什么 FHIR 格式统一了数据还是换不动2014 年 ONC 发布十年愿景白皮书之后医疗信息交换从学术议题变成了国家层面的基础设施问题。国内医院信息化起步不晚北大人民医院 1996 年就用 HL7 集成了 HIS、PACS、RIS、LIS但二十多年过去了真正能说清跨院区、跨厂商、跨语义层级的数据交换做到什么程度的医院并不多。原因很直接HL7 v2 的消息结构过于松散FHIRFast Healthcare Interoperability Resources虽然提供了规范的 RESTful API 和资源模型但它只解决了数据长什么样的问题没解决数据是什么意思的问题。也就是说FHIR 定义了 Patient 资源里要有 name、birthdate、gender但它不会告诉你PaName和Patient.name是同一个东西。我们的做法是在 FHIR 标准框架之上引入疾病本体Disease Ontology, DO和本体映射技术把医疗机构数据库里的字段名、术语、编码通过映射和迁移统一到 FHIR 资源模型下再用 DO 的标准化概念约束疾病术语的表达。这套方案的适用对象很明确正在做区域卫生信息平台互联互通、电子健康档案共享、或者医共体内部业务协同的技术团队尤其是那些被数据模型不开放、无法实现交换共享卡住的项目。本文从模型设计、映射算法、迁移规则到实验验证把完整链路拆开讲代码和参数都是可以直接落地验证的。2. 把 FHIR Resource 拆成本体构建思路与 Protégé 实操2.1 FHIR 资源模型选型为什么首选 Patient 和 ObservationFHIR 标准由基础文档、开发实现方法和资源列表三部分构成。资源列表按 Foundation、Base、Clinical、Financial、Specialized 五大类组织每个资源都附带成熟度标记数字越大表示越稳定N 表示 Normative 级别。工程实践中没必要把全部资源都建成本体只挑成熟度最高、跨系统复用最频繁的即可。以 Patient 为例这是 Individual 类别下最成熟的资源之一直接对应医疗信息系统里患者主索引Master Patient Index, MPI的语义。Patient 资源的属性在 https://www.hl7.org/fhir/patient.html 有完整定义核心属性包括 identifier身份证件号、name姓名、birthdate出生日期、gender性别枚举值为 male/female/other/unknown、telecom联系方式等。Observation 则是 Clinical 类别下诊断和检验相关的核心资源用于描述患者的生理指标测量值如血压、血糖、体温等。Observation 资源存在引用关系DiagnosticReport 可以引用 ObservationObservation 可以引用 Patient、Specimen、BodyStructure 等资源形成一个关联网络。// FHIR Patient 资源 JSON 示例STU3 版本 { resourceType: Patient, id: pat001, identifier: [{ use: official, system: urn:oid:2.16.860.1.113883.2.18.4.1, value: 110101199003074531 }], name: [{ family: Zhang, given: [Wei] }], gender: male, birthDate: 1990-03-07, telecom: [{ system: phone, value: 13800138000, use: mobile }] }参数说明identifier 里 system 用的是 OID 格式表示编码体系的命名空间国内项目可以替换为医院自定义的 OID 根如 2.16.860 是中国国家 OID 根节点birthDate 严格遵循 ISO 8601 日期格式不允许带时间gender 的枚举值只有四个自定义扩展需通过 extension 元素实现不能直接加值。2.2 医疗领域本体的构建类目设计与属性定义领域本体构建常用方法有 7 步法、METHONTOLOGY 法、IDEF5 法等我们在项目里用的是 7 步法变体核心流程分六步明确主题和范围、确定核心概念集、定义概念间关系、本体编码、实例化、逻辑检测与评价。医疗领域本体定义为 O1顶层概念按医疗机构的业务域划分共设六个核心类patient患者主索引信息包含 PaId、PaName、PaGender、PaAge、PaTel、PaStatus 等数据属性EMRs电子病历记录包含 dateofConsul、PaHistory 等数据属性diagnostic诊断信息包含 DiComplaints主诉、DiDepartCode就诊科室编码、DiDepartName就诊科室名称等数据属性Imaging医学影像信息包含影像序列、检查类型等属性medication用药信息包含药品编码、剂量、频次等属性financial费用结算信息包含支付方式、费用明细等属性。对象属性描述类与类之间的语义关系例如 patient 与 EMRs 之间定义 hasEMRsdiagnostic 与 patient 之间定义 has_recordpatient 与 Imaging 之间定义 hasInspection。数据属性则绑定到具体类上描述实例的字符值特征。属性定义完成后用 Protégé 5.5.0 构建表示语言用 OWL本体文件以 RDF/XML 格式导出。!-- 医疗领域本体中 patient 类的数据属性定义OWL 片段 -- owl:DatatypeProperty rdf:abouthttp://www.medical-onto.org#PaName rdfs:domain rdf:resourcehttp://www.medical-onto.org#patient/ rdfs:range rdf:resourcehttp://www.w3.org/2001/XMLSchema#string/ /owl:DatatypeProperty owl:ObjectProperty rdf:abouthttp://www.medical-onto.org#hasEMRs rdfs:domain rdf:resourcehttp://www.medical-onto.org#patient/ rdfs:range rdf:resourcehttp://www.medical-onto.org#EMRs/ /owl:ObjectProperty2.3 资源之间的引用关系建模从 UML 类图到 OWL 对象属性FHIR Resource 之间存在大量引用关系比如 DiagnosticReport 引用 ObservationObservation 引用 Patient 和 SpecimenServiceRequest 引用 DiagnosticReport。在 OWL 本体内这些引用关系不是简单的属性赋值而是通过对象属性的 domain 和 range 约束来表达。在 Protégé 中建模对象属性时建议遵循一个命名约定hasReference 表示资源的直接引用hasSubClass 表示类目从属关系hasRecord 表示病历记录归属。以 Observation 资源为例它引用 Patient 的方式是通过 subject 元素实现的FHIR 官方定义 subject 的类型为 Patient、Group、Device 等可替换类型。在本体建模中我们将 subject 定义为对象属性domain 设为 Observationrange 设为 Patient。图 6 中的引用关系包含 Observation、Media、DiagnosticReport、Specimen、ImagingStudy、BodyStructure、ServiceRequest 和 MolecularSequence 共八个资源这些引用关系在原论文实验环节全部转化为 OWL 对象属性供后续映射使用。实际建模时容易踩坑的地方是属性的传递性。FHIR 中资源间的引用是单向的但 OWL 对象属性可以设置传递性transitive如果贸然将 hasSubClass 设为传递属性会导致推理时出现意外的类目层级合并。经验做法是hasReference 不设传递性hasSubClass 可以设传递性但要用 DisjointClasses 约束避免类目冲突。建模完成之后用 Protégé 自带的 HermiT 推理机做一致性检测检查是否有概念矛盾或属性冲突这一步在 Protégé 的 Reasoner 菜单下即可完成。3. 本体映射与迁移从 PaName 到 Patient.name 的完整链路3.1 映射的形式化定义概念、属性、实例三层对齐本体映射的目标是发现两个本体实体之间的语义对应关系。将医疗领域本体记为 O1FHIR Resource 本体记为 O2形式化定义如下O {C, P, Hc, Hp}其中 C 为概念集合P 为属性集合Hc 为概念间的层次化语义关系Hp 为属性间的层次化语义关系。映射结果 A 是所有对齐集合的并集具体的数学表达参见原论文公式1。从工程视角看更关心的问题是映射怎么落地。论文采用的映射策略分成两步第一步映射抽取第二步映射筛选。映射抽取阶段由于医疗领域本体和 FHIR 本体都是人工策划的curated ontology可直接使用自动匹配器 YAM 或 LogMap 生成初始候选集。这一步要求两个本体的 OWL 文件格式一致建议统一用 RDF/XML 格式避免因序列化格式差异导致匹配器解析失败。映射筛选阶段采用递归方法如果一个映射的目标概念和候选集中已有映射的目标概念相同则该映射也加入候选集。这个策略在实际运行中有个明显的好处能处理间接关联的概念对。比如患者主索引和Patient.identifier不是直接映射但患者主索引映射到identifieridentifier又映射到Patient.identifier通过递归可以完成间接映射的发现。3.2 相似度计算余弦相似度的 Java 实现与参数调优原论文选用余弦相似度计算概念和属性的相似性。核心想法是将两个实体的名称和属性描述向量化计算向量夹角的余弦值值越接近 1 表示相似度越高。对于短文本如PaName vs Patient.name需要先做分词和规范化处理。我们实现的策略是将实体名拆成 token 序列分别计算字面相似度和语义相似度然后加权求和作为最终得分。import java.util.HashMap; import java.util.Map; public class CosineSimilarity { /** * 计算两个实体名的余弦相似度 * 将字符串拆分为字符级 token * 使用字符二元组bigram作为特征项 * param s1 医疗领域本体实体名如 PaName * param s2 FHIR 资源属性名如 Patient.name * return 相似度得分范围 0.0 ~ 1.0 */ public static double compute(String s1, String s2) { if (s1 null || s2 null || s1.isEmpty() || s2.isEmpty()) return 0.0; // 归一化统一转小写去除空格和下划线 s1 s1.toLowerCase().replaceAll([\\s_], ); s2 s2.toLowerCase().replaceAll([\\s_], ); if (s1.equals(s2)) return 1.0; MapString, Integer vec1 tokenize(s1); MapString, Integer vec2 tokenize(s2); double dotProduct 0.0; for (String key : vec1.keySet()) { if (vec2.containsKey(key)) { dotProduct vec1.get(key) * vec2.get(key); } } double norm1 0.0; for (int value : vec1.values()) norm1 value * value; double norm2 0.0; for (int value : vec2.values()) norm2 value * value; if (norm1 0.0 || norm2 0.0) return 0.0; return dotProduct / (Math.sqrt(norm1) * Math.sqrt(norm2)); } /** * 将字符串拆分为二元组特征 * 示例PaName - [pa, an, na, am, me] */ private static MapString, Integer tokenize(String text) { MapString, Integer vector new HashMap(); for (int i 0; i text.length() - 1; i) { String token text.substring(i, i 2); vector.put(token, vector.getOrDefault(token, 0) 1); } return vector; } }特征选择说明字符二元组bigram是处理医疗术语短文本的常用方式比单字符特征更能捕捉词干信息又比词级特征更抗拼写变体。实验结果表明PaName和Patient.name的相似度为 0.63PaId和Patient.identifier为 0.80ObCategory和Observation.category为 0.81。如果不满意可以做仿射变换或引入同义词词典。阈值设定方面0.8 以上直接判定为高置信度映射0.50.8 之间需要人工确认0.5 以下的映射丢弃。这个阈值体系在我们实测中可控的不需要额外标注数据来调参。3.3 迁移规则的工程落地规则 1 与规则 2 的判定逻辑映射完成之后进入迁移环节。迁移的核心思想是根据映射候选集把 O1 的实例数据填充到 O2 的数据属性下。原论文定义了两条迁移规则这里展开讲清楚实现细节。规则 1 处理的是概念映射对存在且匹配对象为下位类的情形。例如FHIR 中 Patient 类的层级是 Resource → DomainResource → Patient而医疗领域本体中 patient 类为一级类目当二者相似度最高时保持 FHIR 原有类目层级不变将 O1 中 patient 类下的 PaName 实例迁移到 FHIR 的 Patient.name 数据属性下。翻译成实际操作步骤如下# 迁移规则 1 的伪代码实现 for concept_mapping in filtered_mapping_set: if concept_mapping.similarity_score 0.6: continue # 相似度过低不做迁移 if concept_mapping.source_concept patient and concept_mapping.target_concept Patient: # 遍历 O1 中 patient 类的全部实例 for instance in source_ontology.instances(patient): source_value instance.get_data_property(PaName) target_property Patient.name # 实例化 FHIR Patient 资源 fhir_patient create_fhir_resource(Patient) fhir_patient.set_property(target_property, source_value)规则 2 处理的是部分属性匹配失败的场景。当概念映射成功但概念下个别数据属性无法在 FHIR 中找到对应属性时FHIR 实例需要添加空数据项占位保证资源结构的完整性如果数据类型完全匹配则不需要变换数据项名称直接迁移即可。医疗领域本体中的 PaGender 与 FHIR 中的 Patient.gender 语义接近但取值逻辑不同PaGender 是布尔类型true 表示男false 表示女而 FHIR 要求枚举值 male/female。这种情况下不能直接拷贝数据需要增加一层枚举值映射逻辑gender_value_map { true: male, false: female } fhir_patient.set_property(Patient.gender, gender_value_map[str(instance.get_data_property(PaGender))])这个细节经常被忽略。很多团队在做数据迁移时只做了字段名映射忽略了取值域的语义转换导致 FHIR 资源生成后校验失败。4. DO 疾病本体从中文诊断到 SNOMED CT 标准编码4.1 为什么选择了 DO跨术语表的枢纽地位疾病术语标准化是信息交换深水区一个诊断结论在 HIS 里可能记作心肌梗死在 LIS 里写成急性心肌梗死在病案首页里编码为 I21.9这三个说法对不上。DO 的优势在于它的类目结构不吃只认一套标准的亏DO 的每个疾病概念都带有多重交叉引用Xrefs包括 MeSH、ICD-10CM、SNOMED CT、UMLS 等天然适合作为中间对齐的枢纽。把迁移后的 FHIR 资源实例和 DO 疾病概念连接采用的映射方法是 owl:equivalentClass 和 owl:sameAs。映射集合的格式为患者 ID诊断结论疾病即通过患者 ID 关联诊断结论再将诊断结论映射到 DO 的标准化疾病编码。以心脏动脉瘤为例DO 的标准化编码为 DOID:13921对应 SNOMED CT 编码 65340007这个编码在不同国家、不同系统之间是一致的。4.2 中文诊断术语到 DO 编码的映射过程把中文疾病术语映射到 DO 概念不能做整串字符串匹配要先做分词和术语规范化。例如诊断结论心脏动脉瘤分词后得到心脏和动脉瘤先匹配 DO 概念名中的 heart aneurysm找不到时使用 DO 的同义词列表做扩展匹配DO 中 heart aneurysm 的 synonyms 包括 cardiac aneurysm、 aneurysm of heart 等。再通过 DO 的 Xrefs 交叉引用获取 SNOMED CT 编码。查询 DO 数据库使用 SQL我们基于 doid.obo 文件解析成 MySQL 表结构核心表字段包括doid_id、name、definition、synonyms、xrefs、parent_doid、subset。以下是将中文术语映射到 DO 编码的查询逻辑-- 查询 DO 概念及其 SNOMED CT 交叉引用编码 SELECT d.doid_id, d.name AS do_name, d.definition, x.xref_code AS snomed_ct_code FROM do_terms d LEFT JOIN do_xrefs x ON d.doid_id x.doid_id WHERE d.name LIKE %aneurysm% AND d.name LIKE %heart% AND x.xref_source SNOMEDCT_US;参数说明do_xrefs 表存储 DO 概念的外部交叉引用xref_source 字段标识编码来源SNOMEDCT_US 表示美国 SNOMED CT 子集如果用 ICD-10CM 编码做匹配将 xref_source 改为 ICD10CM 即可同时要注意 xref_code 和主编码的关系一个 DOID 可能对应多个 SNOMED CT 编码需要根据语义精确度选择最合适的一个。这套查询逻辑在真实项目中运行稳定但是有个前提依赖原始诊断术语的质量。如果 HIS 系统里面记录的是冠心病而不是冠状动脉粥样硬化性心脏病分词和匹配都会出问题。解决的办法是建立院内诊断术语表把医生常用的口语化诊断和标准术语做映射存储为一张术语对照表后续匹配先查这张表再做 DO 映射。4.3 编码结果的存储与交换格式完成 DO 映射后把标准化编码写回 FHIR 资源中在 Condition 资源或 Observation 资源里通过 code 元素携带。FHIR 的 CodeableConcept 数据类型提供了 coding 数组可以同时携带多套编码体系这是它在语义层面优于 V2 消息的重要原因{ resourceType: Condition, subject: { reference: Patient/pat001 }, code: { coding: [ { system: http://purl.obolibrary.org/obo/doid.owl, code: DOID:13921, display: heart aneurysm }, { system: http://snomed.info/sct, code: 65340007, display: Aneurysm of heart } ], text: 心脏动脉瘤 } }原论文的存储方案是通过 Java 编程将迁移和编码后的数据存入 MySQL 数据库建表逻辑和上述相似。如果做的是 SaaS 平台还可以考虑把 FHIR 数据转存到 MongoDB用文档模型存储嵌套的 coding 数组查询时用聚合管道展开 coding 字段做检索。但对中小规模的区域卫生平台MySQL 足够不需要引入额外的文档数据库依赖。5. 实验验证与工程化踩坑清单5.1 相似度计算实验观察阈值变化对映射质量的影响实验环境MAC OS 操作系统MyEclipse 平台Java 语言。样本集来自医享网公开病例医疗领域本体部分数据属性共 40 余个。相似度计算结果中Observation 概念的相似度达 0.98Practitioner 为 0.92Entities 和 EMRs 的相似度低至 0.18说明即使共用一个字母前缀语义完全无关的术语也能被排除。以下是实际运行结果中需要人工干预的部分FHIR 概念最优匹配概念概念相似度FHIR 属性最优匹配属性属性相似度建议操作Patientpatient0.98Patient.identifierPaId0.80自动迁移Practitionerpractitioner0.92Patient.genderPaGender0.77枚举值转换Observationobservation0.98Patient.namePaName0.63人工确认MedicationRequestmedication0.88Patient.birthdatePaAge0.38需要业务映射SpecimenBloodSample0.41Patient.telecomPaTel0.51重新评估注意 Patient.birthdate 和 PaAge 相似度只有 0.38原因在于 birthdate 是绝对日期PaAge 是年龄值语义完全不同不能通过映射自动转换。实际处理方案是PaAge 保留在扩展字段Patient.birthdate 从 EMRs 中的 dateofConsul 或身份证号推算如果身份证号可用则直接解析出生日期解析代码使用正则截取第 7 到 14 位再做格式校验。5.2 常见问题Protégé 推理卡死、概念漂移与同义词扩展Protégé 中加载大型本体时HermiT 推理机可能长时间无响应。经验做法是调大 JVM 堆内存在启动参数中加 -Xmx4g关闭不需要的推理任务只保留一致性检测用 ELK 推理机替代 HermiTELK 专注于 EL 配置文件速度比 HermiT 快一个数量级。概念漂移是另一个高频问题同一诊断术语在不同科室含义不同映射结果需要按科室维度做修正。推荐做法是建立科室术语映射表结构为科室编码来源术语目标 DOID映射置信度查询优先级为科室级别高于全院级别。同义词扩展方面DO 本体自带 synonyms 列表匹配时建议做小写归一化和全半角转换中英文分号、逗号的差异容易导致匹配失败。5.3 从论文到生产FHIR 版本差异与 API 设计论文实验基于 FHIR STU3 版本STU3 与 R4 在资源定义上存在不兼容变更最典型的是 Observation 资源的 category 元素从 CodeableConcept 改成了可复用的 CodeableConcept 列表Patient 资源的 gender 枚举在 R4 中增加了 additional 取值。构建本体时以哪一版为准直接决定后续映射和迁移的结果。建议以 R4 为基准因为 R4 是当前 FHIR 的正式发布版本STU3 的兼容性可以通过 FHIR 官方的版本转换工具处理。API 设计推荐使用 HAPI FHIR 开源库它提供了完整的 R4 资源模型和 RESTful 服务端实现将 MySQL 作为持久化存储HAPI 的 JPA 模块支持直接把 FHIR 资源映射到关系表。关键配置是 validation-mode 设置为 STRICT这能保证写入的资源严格符合 FHIR 规范避免脏数据进入交换库。如果希望在 FHIR 资源和 DO 编码之间的联合查询做到性能可控建议在 MySQL 中建一张扁平化索引表字段包括 resource_type、resource_id、code_system、code_value、display_name为 code_value 加普通索引查询时直接走索引避免全表扫描。在独立使用 FHIR 标准与结合本体的方案之间后者的优势在于让映射工作可重复、可审计。映射规则和迁移规则是显式定义的不需要依赖某个开发人员对业务的个人理解这对于医院信息科的人员流动和项目交接尤其重要。在实际项目中建议先选择 23 个高频互操作场景如患者主索引同步、检验报告共享、诊断术语归一化做试点跑通后再扩展到更多资源类型避免一上来就做全量映射大水漫灌。本文还有配套的精品资源点击获取
返回列表