Press "Enter" to skip to content

使用贝叶斯网络预测医院辅助服务量

使用诊断性输入变量的Python示例

来自Unsplash的照片,作者EJ Strat

自从我开始处理医疗数据(已经快10年了),预测未来的患者数量一直是个难题。需要考虑的依赖关系太多了——患者需求和严重程度、行政需求、检查室限制、医生突然请假、暴风雪天气等等。此外,意外情况可能对排班和资源分配产生连锁影响,与最好的Excel预测相矛盾。

从数据角度解决这类问题非常有趣,一方面因为它们很难,可以深入研究一段时间,另一方面,即使微小的改进也可能带来巨大的收益(例如,提高患者吞吐量、缩短等待时间、使医生更满意、降低成本)。

那么如何解决这个问题呢?嗯,Epic为我们提供了大量数据,包括患者预约到达的实际记录。有了已知的历史输出,我们主要处于监督学习的领域,而贝叶斯网络(BNs)是良好的概率图模型。

虽然大多数决策可以基于单个输入进行(例如,“我应该带雨衣吗?”如果输入是“下雨”,那么决策是“是”),但BN可以轻松处理更复杂的决策——涉及多个输入、概率和依赖关系不同的决策。在本文中,我将使用Python“草稿”一个超简单的BN,它可以根据三个因素的已知概率(症状、癌症分期和治疗目标)为患者在两个月内到达的概率得分。

理解贝叶斯网络:

在其核心,贝叶斯网络是使用有向无环图(DAG)的图形表示联合概率分布。DAG中的节点表示随机变量,有向边表示这些变量之间的因果关系或条件依赖关系。对于所有数据科学项目来说,与利益相关者在开始阶段花费大量时间,以正确映射涉及决策的工作流程(例如变量)对于高质量的预测至关重要。

因此,我将发明一个场景,我们与乳腺肿瘤学合作伙伴会面,并解释了三个变量对于确定患者在两个月内是否需要预约非常关键:他们的症状、癌症分期和治疗目标。我正在输入时进行杜撰,但让我们继续。

(实际上,将有数十个影响未来患者数量的因素,有些是单一或多重依赖关系,其他因素完全独立但仍然有影响)。

使用贝叶斯网络预测医院辅助服务量 四海 第2张

我会说工作流程看起来像上面这样:分期取决于他们的症状,但治疗类型与这些独立,并且还会影响在两个月内的预约。

基于此,我们将从数据源(对我们来说是Epic)获取这些变量的数据,其中再次包含我们的得分节点(Appointment_2months)的已知值,标记为“yes”或“no”。

# 安装包import pandas as pd # 用于数据操作 import networkx as nx # 用于绘制图形import matplotlib.pyplot as plt # 用于绘制图形!pip install pybbn# 用于创建贝叶斯信念网络(BBN)from pybbn.graph.dag import Bbnfrom pybbn.graph.edge import Edge, EdgeTypefrom pybbn.graph.jointree import EvidenceBuilderfrom pybbn.graph.node import BbnNodefrom pybbn.graph.variable import Variablefrom pybbn.pptc.inferencecontroller import InferenceController# 通过手动输入概率创建节点Symptom = BbnNode(Variable(0, 'Symptom', ['非恶性', '恶性']), [0.30658, 0.69342])Stage = BbnNode(Variable(1, 'Stage', ['三期四期', '一期二期']), [0.92827, 0.07173,                                                       0.55760, 0.44240])TreatmentTypeCat = BbnNode(Variable(2, 'TreatmentTypeCat', ['辅助/新辅助', '治疗', '疗法']), [0.58660, 0.24040, 0.17300])Appointment_2weeks = BbnNode(Variable(3, 'Appointment_2weeks', ['否', '是']), [0.92314, 0.07686,                                                 0.89072, 0.10928,                                                 0.76008, 0.23992,                                                 0.64250, 0.35750,                                                 0.49168, 0.50832,                                                 0.32182, 0.67818])

以上,让我们手动为每个变量(节点)输入一些级别的概率分数。在实际应用中,您可以使用交叉表来实现这一点。

例如,对于症状变量,我将获得它们的两个级别的频率,约为31%是非恶性的,69%是恶性的。

照片作者

然后,我们考虑下一个变量,阶段(Stage),并将其与症状进行交叉表分析,得到以下频率。

照片作者

依此类推,直到定义了所有父子对之间的交叉表。

现在,大多数贝叶斯网络包括许多父子关系,因此计算概率可能变得繁琐(且容易出错)。下面的函数可以为任何具有0、1或2个父节点的子节点计算概率矩阵。

# 此函数用于计算概率分布,用于贝叶斯网络(注意,可以处理最多2个父节点)def probs(data, child, parent1=None, parent2=None):    if parent1==None:        # 计算概率        prob=pd.crosstab(data[child], 'Empty', margins=False, normalize='columns').sort_index().to_numpy().reshape(-1).tolist()    elif parent1!=None:            # 检查子节点是否有1个或2个父节点            if parent2==None:                # 计算概率                prob=pd.crosstab(data[parent1],data[child], margins=False, normalize='index').sort_index().to_numpy().reshape(-1).tolist()            else:                    # 计算概率                prob=pd.crosstab([data[parent1],data[parent2]],data[child], margins=False, normalize='index').sort_index().to_numpy().reshape(-1).tolist()    else: print("概率频率计算错误")    return prob  

然后,我们使用我们之前的函数自动计算概率来创建实际的贝叶斯网络节点和网络本身。

# 使用我们之前的函数自动计算概率来创建节点Symptom = BbnNode(Variable(0, '症状', ['非恶性', '恶性']), probs(df, child='SymptomCat'))Stage = BbnNode(Variable(1, '阶段', ['I期/II期', 'III期/IV期']), probs(df, child='StagingCat', parent1='SymptomCat'))TreatmentTypeCat = BbnNode(Variable(2, '治疗类型', ['辅助/新辅助', '治疗', '疗法']), probs(df, child='TreatmentTypeCat'))Appointment_2months = BbnNode(Variable(3, '两个月预约', ['否', '是']), probs(df, child='Appointment_2months', parent1='StagingCat', parent2='TreatmentTypeCat'))# 创建网络bbn = Bbn() \    .add_node(Symptom) \    .add_node(Stage) \    .add_node(TreatmentTypeCat) \    .add_node(Appointment_2months) \    .add_edge(Edge(Symptom, Stage, EdgeType.DIRECTED)) \    .add_edge(Edge(Stage, Appointment_2months, EdgeType.DIRECTED)) \    .add_edge(Edge(TreatmentTypeCat, Appointment_2months, EdgeType.DIRECTED))# 将BBN转换为连接树join_tree = InferenceController.apply(bbn)

这样我们就准备好了。现在让我们通过我们的贝叶斯网络运行一些假设,并评估输出结果。

评估贝叶斯网络输出

首先,让我们查看每个节点的概率,而不声明任何特定条件。

# 定义一个打印边际概率的函数# 每个节点的概率def print_probs():    for node in join_tree.get_bbn_nodes():        potential = join_tree.get_bbn_potential(node)        print("节点:", node)        print("值:")        print(potential)        print('----------------')        # 使用上述函数打印边际概率print_probs()

节点: 1|阶段|I期/II期,III期/IV期值:1=I期/II期|0.671241=III期/IV期|0.32876----------------节点: 0|症状|非恶性,恶性值:0=非恶性|0.693420=恶性|0.30658----------------节点: 2|治疗类型|辅助/新辅助,治疗,疗法值:2=辅助/新辅助|0.586602=治疗|0.173002=疗法|0.24040----------------节点: 3|两个月预约|否,是值:3=否|0.776553=是|0.22345----------------

意思是,这个数据集中的所有患者有67%的概率是Stage_I_II,有69%的概率是非恶性的,有58%的概率需要辅助/新辅助治疗,只有22%的患者需要在两个月后预约。

我们可以从简单的频率表中轻松得到这些结果,而不需要贝叶斯网络。

但是,现在,让我们问一个更具条件性的问题:如果一个患者的Stage = Stage_I_II且TreatmentTypeCat = Therapy,那么他们在两个月内需要接受治疗的概率是多少?同时考虑到提供者目前对他们的症状一无所知(也许他们还没有看过患者)。

我们将运行已知的节点:

# 添加已发生事件的证据,以便可以重新计算概率分布def evidence(ev, nod, cat, val):    ev = EvidenceBuilder() \    .with_node(join_tree.get_bbn_node_by_name(nod)) \    .with_evidence(cat, val) \    .build()    join_tree.set_observation(ev)# 添加更多证据evidence('ev1', 'Stage', 'Stage_I_II', 1.0)evidence('ev2', 'TreatmentTypeCat', 'Therapy', 1.0)# 打印边际概率print_probs()

返回结果如下:

节点:1|Stage|Stage_I_II,Stage_III_IV值:1=Stage_I_II|1.000001=Stage_III_IV|0.00000----------------节点:0|Symptom|Non-Malignant,Malignant值:0=Non-Malignant|0.576020=Malignant|0.42398----------------节点:2|TreatmentTypeCat|Adjuvant/Neoadjuvant,Treatment,Therapy值:2=Adjuvant/Neoadjuvant|0.000002=Treatment|0.000002=Therapy|1.00000----------------节点:3|Appointment_2months|No,Yes值:3=No|0.890723=Yes|0.10928----------------

该患者只有11%的概率在两个月后到达。

关于质量输入变量重要性的说明:

贝叶斯网络在提供可靠的未来就诊估计方面的成功,极大地依赖于对患者护理工作流程的准确映射。类似的患者在类似的情况下通常需要类似的服务。这些输入的排列,其特征可以从临床到行政,最终对应于一条在某种程度上确定性的服务需求路径。但是,时间预测越复杂或越远,就越需要具有高质量输入的更具特定性和复杂性的贝叶斯网络。

原因如下:

  1. 准确的表示:贝叶斯网络的结构必须反映变量之间的实际关系。选择不当的变量或误解的依赖关系可能导致不准确的预测和洞察。
  2. 有效的推理:质量输入变量增强了模型执行概率推理的能力。当变量基于它们的条件依赖性准确连接时,网络可以提供更可靠的洞察。
  3. 降低复杂性:包括无关或多余的变量可能不必要地复杂化模型并增加计算要求。质量输入简化了网络,使其更高效。

感谢阅读。很高兴与LinkedIn上的任何人进行联系!如果您对数据科学和医疗保健的交叉领域感兴趣,或者如果您有有趣的挑战要分享,请留言或发送私信。

请查看我的其他文章:

为什么平衡类别被过度夸大了

特征工程CPT代码

设计基本神经网络的7个步骤

Leave a Reply

Your email address will not be published. Required fields are marked *