患者旅程

患者旅程是指患者在医疗系统中经历的过程。它可以是针对某种特定疾病的旅程,也可以是贯穿一生的历程。

简介

患者旅程数据本质上既高度异构又深度互联,涵盖了临床记录、实验室结果、用药情况、影像资料、生活方式因素以及社会健康决定因素。

从定义上讲,这些多样化的数据点构成了一个复杂的网络关系,例如:患者的饮食习惯影响代谢状况,运动模式与心血管预后相关,环境暴露与呼吸系统疾病相连。传统的关联数据库难以高效地捕获和查询这些错综复杂的、多维度的连接,通常需要跨越数十张表进行复杂的关联查询(Join)。

Neo4j 灵活的、模式可选的图数据库模型在此领域表现卓越,它能够自然地将患者、病情、治疗方案和生活方式因素表示为节点,并通过镜像现实世界临床因果和相关性的关系进行连接。这种方法使医疗机构能够无缝整合分散的数据源,同时保持全面的患者旅程分析所必需的语义丰富度。

场景

在本场景中,我们将探讨如何利用 Neo4j 对不同患者在医疗系统中的多个旅程进行建模和分析。这包括追踪与各医疗服务提供者的互动、所获诊断以及实施的治疗。这绝非详尽的示例,而是一个起点,用以展示 Neo4j 如何应用于患者旅程用例。

patient journey
图 1. 患者旅程示例

解决方案

患者旅程记录了个人在一段时间内通过多个接触点、服务提供者和治疗方案所经历的完整医疗体验。Neo4j 图数据库擅长将来自不同电子健康记录(EHR)、实验室结果、处方和专家门诊的碎片化患者数据,连接成一个统一的、关系丰富的数据模型。

这种方法使医疗机构能够可视化复杂的护理路径,识别治疗差距,了解不同干预措施如何影响治疗结果,并优化患者福利。

图查询可以实时遍历患者的完整病史,从而挖掘出传统数据库难以发现的模式,如药物相互作用、转诊瓶颈或护理协调问题。最终,Neo4j 使临床医生和管理人员能够提供更个性化、连续性的护理,同时优化整个医疗网络中的资源分配。

建模

在 Neo4j 中对患者旅程进行建模,需要定义代表患者医疗体验中关键实体和交互的节点与关系。

数据模型

Neo4j 的标签属性图模型通过将临床概念表示为节点(PersonConditionDrugProcedure),将其属性表示为属性(日期、代码、值),将临床事件表示为关系(HAS_CONDITIONPRESCRIBED_DRUG),从而与 OMOP 通用数据模型自然对齐。这使得在保留 OMOP 词汇表定义的患者、诊断、治疗和预后之间丰富的语义连接的同时,能够直观地映射标准化的观察性健康数据。

概述

该模型捕获了一个简化的患者旅程,展示了如何对其进行表示。

FIUC BLS PatientJourney Simple
图 2. 患者旅程模型概览

节点

以下节点用于患者旅程数据模型:

  • Patient(患者) - 接受医疗服务的特定个人。

  • Encounter(就诊) - 患者与医疗系统之间的一次具体互动,例如住院或门诊预约。

  • Observation(观察指标) - 在就诊期间记录的临床测量或发现,例如生命体征或实验室结果。

  • Provider(医疗服务提供者) - 参与患者护理的医疗专业人员,例如医生或护士。

  • Speciality(专科) - 与服务提供者相关的医学专科,例如心脏科或全科。

  • Organisation(机构) - 医疗机构或设施,例如 Provider 所属的医院或诊所。

  • Drug(药物) - 在就诊期间为患者开具的药物。药物通过代码(如 SNOMED CT、RxNorm)与剂量组合进行唯一识别。

  • Condition(病情) - 在就诊期间确定的医学诊断或健康状况。

关系

以下关系用于患者旅程数据模型:

  • HAS_ENCOUNTER - 连接 Patient 与其 Encounter(就诊)。

  • HAS_OBSERVATION - 连接 Encounter 与其记录的 Observation(观察指标)。

  • PRESCRIBED - 连接 Encounter 与在该次就诊期间开具的 Drug(药物)。

  • DIAGNOSED - 连接 Encounter 与在该次就诊期间确诊的 Condition(病情)。

  • ATTENDED_BY - 连接 Encounter 与在该次就诊期间照料患者的 Provider(服务提供者)。

  • HAS_SPECIALITY - 连接 Provider 与其 Speciality(专科)。

  • BELONGS_TO - 连接 Provider 与其所属的 Organisation(机构)。

  • NEXT - 连接 Patient 旅程中顺序发生的 Encounter(就诊)。

演示数据

此 Cypher 语句将在 Neo4j 数据库中创建示例图。

// MERGE patients
MERGE (pas:Patient {id: '987-223-1345', name: 'Alice Smith', birthDate: date('1975-03-22')})
MERGE (pct:Patient {id: '987-223-1346', name: 'Charlotte Tegreba', birthDate: date('1992-03-22')})
MERGE (paj:Patient {id: '987-783-1346', name: 'Alex Jerdaschl', birthDate: date('1986-11-14')})

// MERGE providers, specialities and organisation
MERGE (p1:Provider {id: 'prov-001', name: 'Dr. Emily Smith'})
MERGE (p2:Provider {id: 'prov-002', name: 'Dr. Anisha Jones'})
MERGE (p3:Provider {id: 'prov-078', name: 'Dr. Paul Vionnet'})

MERGE (sp1:Speciality {name: 'Cardiology'})
MERGE (sp2:Speciality {name: 'General Practice'})
MERGE (sp3:Speciality {name: 'Nephrology'})

MERGE (org1:Organisation {id: 'org-001', name: 'City Health Clinic', address: '123 Main St, Metropolis'})
MERGE (org2:Organisation {id: 'org-002', name: 'Centre for Kidney Health', address: '498 Main St, Metropolis'})

// MERGE provider relationships
MERGE (p1)-[:HAS_SPECIALITY]->(sp1)
MERGE (p2)-[:HAS_SPECIALITY]->(sp2)
MERGE (p3)-[:HAS_SPECIALITY]->(sp3)

MERGE (p1)-[:BELONGS_TO]->(org1)
MERGE (p2)-[:BELONGS_TO]->(org1)
MERGE (p3)-[:BELONGS_TO]->(org2)

//// Alice Smith encounters and data ////

// MERGE encounter 1 and associated data
MERGE (eas1:Encounter {id: 'enc-as75-001', date: date('2025-11-10'), type: 'Inpatient'})
MERGE (oas11:Observation {id: 'obs-as75-011', code: '8320-2', value: '175/80', unit: 'mmHg'})
MERGE (oas12:Observation {id: 'obs-as75-012', code: '29463-7', value: 68, unit: 'kg'})
MERGE (dras1:Drug {code:'386873009', name: 'Lisinopril', dosage: '10 mg', frequency: 'Once daily'})
MERGE (conas1:Condition {code: '38341003', description: 'Hypertensive disorder, systemic arterial (disorder)'})

// MERGE encounter 2 and associated data
MERGE (eas2:Encounter {id: 'enc-as75-002', date: date('2025-11-12'), type: 'Outpatient'})
MERGE (oas21:Observation {id: 'obs-as75-021', code: '8320-2', value: '140/80', unit: 'mmHg'})
MERGE (oas22:Observation {id: 'obs-as75-022', code: '29463-7', value: 68.1, unit: 'kg'})
MERGE (dras2:Drug {code:'386873009', name: 'Lisinopril', dosage: '10 mg', frequency: 'Once daily'})
MERGE (conas2:Condition {code: '44054006', description: 'Diabetes mellitus type 2 (disorder)'})

// MERGE encounter 3 and associated data
MERGE (eas3:Encounter {id: 'enc-as75-003', date: date('2025-11-24'), type: 'Outpatient'})
MERGE (oas31:Observation {id: 'obs-as75-031', code: '8320-2', value: '120/80', unit: 'mmHg'})
MERGE (oas32:Observation {id: 'obs-as75-032', code: '29463-7', value: 69.6, unit: 'kg'})

// MERGE encounter 1 relationships
MERGE (pas)-[:HAS_ENCOUNTER]->(eas1)
MERGE (eas1)-[:HAS_OBSERVATION]->(oas11)
MERGE (eas1)-[:HAS_OBSERVATION]->(oas12)
MERGE (eas1)-[:PRESCRIBED]->(dras1)
MERGE (eas1)-[:DIAGNOSED]->(conas1)
MERGE (eas1)-[:ATTENDED_BY]->(p1)

// MERGE encounter 2 relationships
MERGE (eas1)-[:NEXT]->(eas2)
MERGE (pas)-[:HAS_ENCOUNTER]->(eas2)
MERGE (eas2)-[:HAS_OBSERVATION]->(oas21)
MERGE (eas2)-[:HAS_OBSERVATION]->(oas22)
MERGE (eas2)-[:DIAGNOSED]->(conas1)
MERGE (eas2)-[:DIAGNOSED]->(conas2)
MERGE (eas2)-[:PRESCRIBED]->(dras2)
MERGE (eas2)-[:ATTENDED_BY]->(p2)

// MERGE encounter 3 relationships
MERGE (eas2)-[:NEXT]->(eas3)
MERGE (pas)-[:HAS_ENCOUNTER]->(eas3)
MERGE (eas3)-[:HAS_OBSERVATION]->(oas31)
MERGE (eas3)-[:HAS_OBSERVATION]->(oas32)
MERGE (eas3)-[:ATTENDED_BY]->(p2)

////  Charlotte Tegreba encounters and data ////

// MERGE encounter 1 and associated data
MERGE (ect1:Encounter {id: 'enc-ct92-001', date: date('2025-11-10'), type: 'Inpatient'})
MERGE (oct11:Observation {id: 'obs-ct92-011', code: '8320-2', value: '175/80', unit: 'mmHg'})
MERGE (oct12:Observation {id: 'obs-ct92-012', code: '29463-7', value: 68, unit: 'kg'})
MERGE (drct1:Drug {code:'386873009', name: 'Lisinopril', dosage: '10 mg', frequency: 'Once daily'})
MERGE (conct1:Condition {code: '38341003', description: 'Hypertensive disorder, systemic arterial (disorder)'})

// MERGE encounter 2 and associated data
MERGE (ect2:Encounter {id: 'enc-ct92-002', date: date('2025-11-12'), type: 'Outpatient'})
MERGE (oct21:Observation {id: 'obs-ct92-021', code: '8320-2', value: '140/80', unit: 'mmHg'})
MERGE (oct22:Observation {id: 'obs-ct92-022', code: '29463-7', value: 68.1, unit: 'kg'})
MERGE (drct2:Drug {code:'386873009', name: 'Lisinopril', dosage: '10 mg', frequency: 'Once daily'})
MERGE (conct2:Condition {code: '44054006', description: 'Diabetes mellitus type 2 (disorder)'})

// MERGE encounter 3 and associated data
MERGE (ect3:Encounter {id: 'enc-ct92-003', date: date('2025-11-24'), type: 'Outpatient'})
MERGE (oct31:Observation {id: 'obs-ct92-031', code: '8320-2', value: '120/80', unit: 'mmHg'})
MERGE (oct32:Observation {id: 'obs-ct92-032', code: '29463-7', value: 69.6, unit: 'kg'})

// MERGE encounter 1 relationships
MERGE (pct)-[:HAS_ENCOUNTER]->(ect1)
MERGE (ect1)-[:HAS_OBSERVATION]->(oct11)
MERGE (ect1)-[:HAS_OBSERVATION]->(oct12)
MERGE (ect1)-[:PRESCRIBED]->(drct1)
MERGE (ect1)-[:DIAGNOSED]->(conct1)
MERGE (ect1)-[:ATTENDED_BY]->(p1)

// MERGE encounter 2 relationships
MERGE (ect1)-[:NEXT]->(ect2)
MERGE (pct)-[:HAS_ENCOUNTER]->(ect2)
MERGE (ect2)-[:HAS_OBSERVATION]->(oct21)
MERGE (ect2)-[:HAS_OBSERVATION]->(oct22)
MERGE (ect2)-[:DIAGNOSED]->(coct1)
MERGE (ect2)-[:DIAGNOSED]->(coct2)
MERGE (ect2)-[:PRESCRIBED]->(dct2)
MERGE (ect2)-[:ATTENDED_BY]->(p2)

// MERGE encounter 3 relationships
MERGE (e2ct)-[:NEXT]->(ect3)
MERGE (pct)-[:HAS_ENCOUNTER]->(ect)
MERGE (ect3)-[:HAS_OBSERVATION]->(oct31)
MERGE (ect3)-[:HAS_OBSERVATION]->(oct32)
MERGE (ect3)-[:ATTENDED_BY]->(p2)


//// Alex Jerdaschl encounters and data ////

// MERGE encounter 1 and associated data
MERGE (eaj1:Encounter {id: 'enc-aj86-001', date: date('2025-11-10'), type: 'Inpatient'})
MERGE (oaj11:Observation {id: 'obs-aj86-011', code: '8320-2', value: '173/80', unit: 'mmHg'})
MERGE (oaj12:Observation {id: 'obs-aj86-012', code: '29463-7', value: 60, unit: 'kg'})
MERGE (draj1:Drug {code:'703674001', name: 'Dapagliflozin', dosage: '10 mg', frequency: 'Once daily'})
MERGE (conaj2:Condition {code: '38341003', description: 'Hypertensive disorder, systemic arterial (disorder)'})
MERGE (con3:Condition {code: '709044004', description: 'Chronic kidney disease (disorder)'})

// MERGE encounter 2 and associated data
MERGE (eaj2:Encounter {id: 'enc-aj86-002', date: date('2025-11-12'), type: 'Outpatient'})
MERGE (oaj21:Observation {id: 'obs-aj86-021', code: '8320-2', value: '140/80', unit: 'mmHg'})
MERGE (oaj22:Observation {id: 'obs-aj86-022', code: '29463-7', value: 68.1, unit: 'kg'})
MERGE (draj2:Drug {code:'386873009', name: 'Lisinopril', dosage: '10 mg', frequency: 'Once daily'})

// MERGE encounter 1 relationships
MERGE (paj)-[:HAS_ENCOUNTER]->(eaj1)
MERGE (eaj1)-[:HAS_OBSERVATION]->(oaj11)
MERGE (eaj1)-[:HAS_OBSERVATION]->(oaj12)
MERGE (eaj1)-[:PRESCRIBED]->(draj1)
MERGE (eaj1)-[:DIAGNOSED]->(conaj3)
MERGE (eaj1)-[:ATTENDED_BY]->(p1)

// MERGE encounter 2 relationships
MERGE (eaj1)-[:NEXT]->(eaj2)
MERGE (paj)-[:HAS_ENCOUNTER]->(eaj2)
MERGE (eaj2)-[:HAS_OBSERVATION]->(oaj21)
MERGE (eaj2)-[:HAS_OBSERVATION]->(oaj22)
MERGE (eaj2)-[:DIAGNOSED]->(conaj3)
MERGE (eaj2)-[:DIAGNOSED]->(conaj2)
MERGE (eaj2)-[:PRESCRIBED]->(draj2)
MERGE (eaj2)-[:ATTENDED_BY]->(p3)

模式 (Schema)

如果调用

// Show neo4j schema
CALL db.schema.visualization()

您将看到以下响应

8 nodes connected with relationships
图 3. 患者旅程模型的模式

Cypher 查询

本节提供了一组示例 Cypher 查询,可用于从不同角度探索患者旅程图。

基于患者的查询

这些查询侧重于从以患者为中心的视角获取信息。

给定一个 Patient,开了哪些 Drug(药物)?

此查询将获取 ID 为 987-223-1345 的患者在整个旅程中被开具的所有药物。

// Get all the drugs a patient has been prescribed
MATCH (p:Patient {id: '987-223-1345'})-[:HAS_ENCOUNTER]->(:Encounter)-[:PRESCRIBED]->(d:Drug)
RETURN p.name AS Name, collect(DISTINCT d.name) AS `Prescribed Drugs`;

给定一个 Patient,诊断出了哪些 Condition(病情)?

对于 ID 为 987-223-1345 的患者,此查询检索他们在医疗旅程中被诊断出的所有病情。

// Get all the conditions a patient has been diagnosed with
MATCH (p:Patient {id: '987-223-1345'})-[:HAS_ENCOUNTER]->(:Encounter)-[:DIAGNOSED]->(c:Condition)
RETURN p.name AS Name, collect(DISTINCT c.description) AS conditions;

给定一个 Patient 及其 Condition - 其他患有相同 ConditionPatient 还有哪些并发症?

在此查询中,我们想要找出其他患者所患疾病的所有并发症。作为医疗服务提供者,这些信息有助于针对性地进行潜在的检查或治疗。

MATCH
    (p:Patient {id: '987-223-1345'})-[:HAS_ENCOUNTER]->(:Encounter)-[:DIAGNOSED]->(c:Condition),
    (c)<-[:DIAGNOSED]-(:Encounter)<-[:HAS_ENCOUNTER]-(other:Patient)-[:HAS_ENCOUNTER]->(:Encounter)-[:DIAGNOSED]->(c2:Condition)
WHERE c <> c2
RETURN c2.code AS Code, c2.description AS Description, count(DISTINCT other) AS PatientCount
ORDER BY PatientCount DESC;

基于病情的查询

这些查询侧重于从以病情为中心的视角获取信息。

特定的 Condition 与哪些并发症相关?

在这里,我们获取与特定病情相关的所有并发症。这对于临床决策支持系统识别常见的共存疾病非常有用。通过按出现频率排序,我们可以查看哪些并发症与给定的病情关联最频繁。

// What are the comorbidities associated with particular `Condition`?
MATCH (c1:Condition {code: '38341003'})<-[:DIAGNOSED]-(:Encounter)-[:DIAGNOSED]->(c2:Condition)
WHERE c1 <> c2
RETURN c2.code AS Code, c2.description AS Description, count(*) AS Occurrences
ORDER BY Occurrences DESC;

基于服务提供者的查询

这些查询侧重于从以服务提供者为中心的视角获取信息。

给定一个 Provider,他们看过哪些 Patient,频率如何?

此查询允许医疗机构通过识别特定服务提供者看过的患者以及这些就诊的频率,来分析提供者的工作量和患者分布。

//* Given a provider what patients have they seen?
MATCH (p:Provider {id: 'prov-002'})<-[:ATTENDED_BY]-(:Encounter)<-[:HAS_ENCOUNTER]-(pt:Patient)
RETURN pt.id AS PatientId, pt.name AS `Patient Name`, count(*) AS Encounters
ORDER BY Encounters DESC;

给定一个 Provider,他们诊断出了哪些 Condition(病情)?

这有助于了解服务提供者处理的病例组合,从而为资源分配、培训需求和质量改进计划提供参考。

// Given a provider what diagnoses have they made?
MATCH (p:Provider {id: 'prov-002'})<-[:ATTENDED_BY]-(:Encounter)-[:DIAGNOSED]->(c:Condition)
RETURN c.code AS Code, c.description AS Description, count(*) AS Diagnoses
ORDER BY Diagnoses DESC;

给定一个 Provider,他们开了哪些 Drug(药物)?

这可用于查看特定服务提供者是否遵循处方指南,或识别可能需要进一步调查的药物使用模式。

// Given a provider what drugs have they prescribed?
MATCH (p:Provider {id: 'prov-002'})<-[:ATTENDED_BY]-(:Encounter)-[:PRESCRIBED]->(d:Drug)
RETURN d.code AS Code, d.name AS Name, count(*) AS Prescriptions

给定一个 Patient,其对应的 Provider 是谁?

患者是否有固定的护理提供者,还是看过许多不同的提供者?此查询有助于识别特定患者在医疗旅程中接触过的所有医疗服务提供者,这对于护理协调和了解患者的护理网络非常有用。

// Get the names of the providers a patient has seen
MATCH (pat:Patient {id: '987-223-1345'})-[:HAS_ENCOUNTER]->(:Encounter)-[:ATTENDED_BY]->(provider:Provider)
RETURN provider.name AS Provider

图数据科学 (GDS)

Neo4j 的图数据科学(Graph Data Science)库利用关系信息实现复杂的患者旅程分析。

  • 社区检测算法通过对具有相似临床轨迹、并发症和治疗反应的个体进行聚类,来识别患者的亚型(sub-phenotypes)。

  • 中心度算法(如 PageRank度中心度)可以突出显示患者病史中的关键实体,例如严重过敏、药物不良反应或关键诊断,这些实体对护理决策和预后有重大影响。

  • 路径查找算法可以通过分析相似患者群体的成功护理路径来揭示最佳治疗序列,而相似性算法则可识别病情相似的患者,用于比较有效性研究。

  • 链接预测功能可以根据历史患者数据中观察到的关系模式,预测潜在的并发症或疾病进展,从而实现主动干预策略。

现实世界示例

这不仅仅是理论,已有现实世界中利用 Neo4j 分析患者旅程的案例。

阿斯利康 (AstraZeneca)

阿斯利康正在使用 Neo4j 通过分析患者旅程来改善患者预后。

利用图算法改善患者预后

利用图算法改善患者预后

演示文稿附带了一篇博客文章

© . This site is unofficial and not affiliated with Neo4j, Inc.