Python课程设计项目-基于机器学习的糖尿病风险预警分析系统

Posted 赖床份子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Python课程设计项目-基于机器学习的糖尿病风险预警分析系统相关的知识,希望对你有一定的参考价值。

这个东西是我大二时候做的,做的挺一般的,当时也没想着搭建界面啥的,测试的也不够,就是单纯的分享一下吧,不足之处大家多多指正,我会把所有的代码和数据在文章最后都放出来,喜欢的话点个赞吧!

[摘 要] 糖尿病是一种全球性的流行性疾病,随着经济生活的高速发展,其患病率急剧增高,已成为威胁人类健康的第三大杀手。糖尿病目前尚不能根治,一旦发生,将终身相伴。糖尿病注重细节管理,尤其是在饮食方面有严格要求,为此,患者将会失去很多生活乐趣和行动自由;糖尿病的慢性并发症具有高度致残性,严重者会对患者的生活和工作造成不利影响;来自于健康和经济的双重压力,给患者本人及其家属造成巨大的精神负担,据统计,糖尿病患者中心理障碍(如抑郁症)的发病率可高达30%-50%,上述种种情况,使患者的生活质量大大降低。为帮助解决糖尿病给患者带来的巨大痛苦,建立起联防预警机制,及时筛查出具有患病风险的高危人群,本系统选取了皮马印第安人糖尿病数据集,利用Python中的随机森林、决策树、逻辑回归等多种机器学习算法,构建起糖尿病风险预警系统,用户可将自身信息输入到系统中,系统会自动分析是否具有糖尿病的风险,为用户和医生进行健康筛查提供帮助。

基于机器学习的糖尿病风险预警分析系统

引言

世界卫生组织近日发布报告称,糖尿病患者的数量正以惊人的速度增长,目前全球每年约有320万人死于糖尿病导致的并发症。高血糖本身并不可怕,真正可怕的是糖尿病所致的各种并发症。如果控制不好的话,患者从头到脚、由内到外、从肉体到精神几乎无一幸免,其对患者的影响是多方面的、严重的和终身性的,与之相伴的还有长期高额的医疗支出,但最终患者还是难逃致残和早亡的结局,说糖尿病是“百病之源”可谓实至名归。

第一章 项目意义

1.1 项目背景

1.1.1 糖尿病发展现状

世界卫生组织近日发布报告称,糖尿病患者的数量正以惊人的速度增长,目前全球每年约有320万人死于糖尿病导致的并发症。报告指出,在大部分发展中国家,每10名35-64岁的成年人中至少有1人死于糖尿病,糖尿病已经成为大多数国家居民早逝的主要死因之一。据统计,糖尿病可使患者平均寿命缩短10年。
  糖尿病目前尚不能根治,一旦发生,将终身相伴;糖尿病注重细节管理,尤其是在饮食方面有严格要求,为此,患者将会失去很多生活乐趣和行动自由;糖尿病的慢性并发症具有高度致残性,严重者会对患者的生活和工作造成不利影响;来自于健康和经济的双重压力,给患者本人及其家属造成巨大的精神负担,据统计,糖尿病患者中心理障碍(如抑郁症)的发病率可高达30%-50%。上述种种情况,使患者的生活质量大大降低。

1.1.2 糖尿病危害

糖尿病给各国政府和人民带来了沉重的经济负担。以美国为例,1987年用于治疗糖尿病的花费是240亿美元,1998年为980亿,2002年上升至1320亿美元。糖尿病患者的年人均医疗消费是10071美元,而非糖尿病患者人均花费则为2699美元。在中国,2002年17省会城市调查显示,糖尿病治疗费188.2亿人民币,约占卫生事业费占4%;普通糖尿病患者每人每年平均花费3726元,有并发症的糖尿病患者每人每年平均花费高达13897元。

在糖尿病的早期阶段,患者除了血糖偏高以外,可能没有任何症状,但如果因此而满不在乎、放松治疗,持续的高血糖可能在不知不觉中,侵蚀您全身的大、小血管及神经,引起体内各个组织器官的病变,导致各种急、慢性并发症,严重者可致残、致死。
1、急性并发症
  以糖尿病酮症酸中毒和非酮症高渗性昏迷最为常见,前者多见于1型糖尿病及处于应激状态(如急性感染、急性脑卒中、精神刺激、外伤、手术、暴饮暴食)的2型糖尿病,后者多见于老年患者。急性并发症往往来势汹汹,如果不能及时救治,病死率很高。随着现代医疗水平的提高,尤其是胰岛素的广泛应用,急性并发症的发病率及病死率较以往显著下降。
2、慢性并发症
  与急性并发症相比,如今慢性并发症发生率越来越高,已成为糖尿病患者致残、致死的首要因素。
  (1)心脏病变。糖尿病可引起冠状动脉硬化、狭窄和堵塞,导致冠心病(心绞痛、心肌梗死)、糖尿病心肌病,甚至猝死。糖尿病引起的心脏病尽管病情较重但症状却往往不典型,无痛性心肌梗死较为多见。糖尿病心肌病最常见的症状是心脏扩大及心律失常,后期则出现心力衰竭。糖尿病患者发生心肌梗死的危险比非糖尿病患者高3-4倍。
  (2)脑血管病变。主要是脑血栓,脑出血则相对少见。轻者出现半身麻木或活动不灵,重则导致瘫痪、神志不清、深昏迷,危及生命。糖尿病患者发生脑卒中者的危险是非糖尿病患者的2-4倍。
  (3)肾脏损害。是糖尿病最常见的微血管并发症之一。在早期阶段,患者症状常不明显,尿微量白蛋白排泄率增加是其唯一表现,很容易被忽略,以后随着肾小球滤过率下降,体内代谢废物不能排出,血肌酐及尿素氮开始升高,并出现临床症状,病情进一步发展可导致尿毒症。
  有资料显示,微量白蛋白尿的出现率在病程10年和20年后可分别达到10%-30%和40%,且20年后有5%-10%的患者进展为尿毒症,糖尿病患者发生尿毒症的危险性是非糖尿病患者的17倍,在接受透析的终末期肾病患者中有一半是糖尿病患者。
  (4)视网膜病变和白内障。视网膜病变最为多见,早期可无任何症状,随着病情进展,导致眼底反复出血、视力明显减退,严重时可导致失明。糖尿病致失明的危险性为非糖尿病患者的25倍。我国资料报道,病程在10年的糖尿病患者有50%发生视网膜病变,病程15年以上者有80%发生视网膜病变,而2%的患者将完全失明。
  (5)神经损害。神经病变神经病变患病率在糖尿病病程为5年、10年、20年后分别可达到30%-40%、60%-70%和90%。感觉神经受损,患者可出现四肢麻木疼痛、感觉丧失、无痛性心肌梗死;自主神经受损可出现静息心率增快、直立性低血压、出汗异常、胃轻瘫、膀胱尿潴留、便秘、腹泻等症状。
  (6)糖尿病足。由于长期高血糖造成下肢血管病变及神经损害,并在此基础上合并感染,导致下肢溃疡及坏疽,病情严重者需要截肢。据统计,因糖尿病足坏疽而截肢者为非糖尿病患者的20倍。据美国相关资料统计,成年截肢患者当中,有40%是糖尿病足坏疽所致,危害性极大。
  (7)各种感染。糖尿病患者由于抵抗力差,容易并发呼吸道及泌尿道感染、肺结核、皮肤黏膜感染及牙周炎。
  (8)对孕产妇及胎儿的损害。如果血糖控制不好,糖尿病孕妇易出现流产、胎儿发育畸形、死胎、新生儿低血糖等妊娠并发症,母亲及胎儿死亡率均较高。

1.2 项目意义与目标

我国的糖尿病患者初诊时约80%已出现慢性并发症,而无并发症的患者约 75%以上尚未被明确诊断,未能做到早期诊断、早期治疗。资料显示 ,由于我国患者病情控制差,绝大部分病人临床诊断时已经出现并发症,因肾、眼、心脑血管、足等病变的致残率及死亡率,均明显高于国外。其中缺乏糖尿病风险预警机制是主要原因之一。

如果开展有效的糖尿病风险预警机制,让患者及时了解自己的身体状况,及时就医,那么大部分患者可以获得早期治疗。现在的许多研究已经证实:有效的病情控制 ,可以减少或延缓并发症的发生、发展,降低致残率,提高患者的生活质量,延长患者的寿命。因此,有效的糖尿病风险预警机制,可取得巨大的社会效益。

本研究在充分调研的前提下,利用皮马印第安人糖尿病数据集,利用Python中的随机森林、决策树、逻辑回归等多种机器学习算法,构建起糖尿病风险预警系统,用户可将自身信息输入到系统中,系统会自动分析是否具有糖尿病的风险,为用户和医生进行健康筛查提供帮助。

第二章关键技术与方法

2.1随机森林算法

作为新兴起的、高度灵活的一种机器学习算法,随机森林(Random Forest,简称RF)拥有广泛的应用前景,从市场营销到医疗保健保险,既可以用来做市场营销模拟的建模,统计客户来源,保留和流失,也可用来预测疾病的风险和病患者的易感性。
  随机森林其本质就是通过集成学习的思想将多棵树集成的一种算法,它的基本单元是决策树,而它的本质属于机器学习的一大分支——集成学习(Ensemble Learning)方法。随机森林的名称中有两个关键词,一个是“随机”,一个就是“森林”。“森林”我们很好理解,一棵叫做树,那么成百上千棵就可以叫做森林了,其实这也是随机森林的主要思想——集成思想的体现。
  其实从直观角度来解释,每棵决策树都是一个分类器(假设现在针对的是分类问题),那么对于一个输入样本,N棵树会有N个分类结果。而随机森林集成了所有的分类投票结果,将投票次数最多的类别指定为最终的输出,这就是一种最简单的 Bagging 思想。

随机森林作为一种很灵活实用的方法,它有如下几个特点:
1.在当前所有算法中,具有极好的准确率;
2.能够有效地运行在大数据集上;
3.能够处理具有高维特征的输入样本,而且不需要降维;
4.能够评估各个特征在分类问题上的重要性;
5.在生成过程中,能够获取到内部生成误差的一种无偏估计;
6.对于缺省值问题也能够获得很好得结果。

2.2决策树算法

决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构建决策树来进行分析的一种方式,是一种直观应用概率分析的一种图解法。决策树,顾名思义需要构建树的结构来进行决策(分类);其实决策树的工作过程和人的思考过程是比较类似的,人类在决策过程中,会基于一系列的判别标准,来对某一事务做出最终的决定。决策树正是基于这一思想,在对数据进行分类的时候,判别标准就是数据的特征,最终的决定就是数据的类别。

首先介绍信息熵(entropy)的概念,信息熵主要用来衡量样本集合的“混乱程度”,度量事物的不确定性。信息熵越大代表样本集合越混乱,不确定性越高,其计算方式如下:

 D代表样本集合;样本集合中数据可以分为k个类别,每个类别的概率为Pi。样本集合中,如果所有的样本均属于同一类别,那么此时“混乱程度”最低,信息熵为0,取到最小值;样本集合中,如果所有类别的样本概率相同,那么此时“混乱程度”最高,信息熵为logk,取到最大值。

2.3逻辑回归算法

逻辑回归虽然带有回归两字,但实际上是做分类任务的,并且是一个经典的二分类算法。在选择使用机器学习算法的时候,通常考虑的就是逻辑回归算法,再去考虑另外复杂的算法,也就是说能用简单的还是用简单的算法。在机器学习中,算法不是越复杂越好,而是要简单、高效、通俗易懂。

逻辑回归其实是一个分类算法而不是回归算法。通常是利用已知的自变量来预测一个离散型因变量的值(像二进制值0/1,是/否,真/假)。简单来说,它就是通过拟合一个逻辑函数(logit fuction)来预测一个事件发生的概率。所以它预测的是一个概率值,自然,它的输出值应该在0到1之间。
    逻辑回归算法原理可以概括为以下几个方面:

  1. 找一个合适的预测函数(Andrew Ng的公开课中称为hypothesis),一般表示为h函数,该函数就是我们需要找的分类函数,它用来预测输入数据的判断结果。这个过程时非常关键的,需要对数据有一定的了解或分析,知道或者猜测预测函数的“大概”形式,比如是线性函数还是非线性函数。
  2. 构造一个Cost函数(损失函数),该函数表示预测的输出(h)与训练数据类别(y)之间的偏差,可以是二者之间的差(h-y)或者是其他的形式。综合考虑所有训练数据的“损失”,将Cost求和或者求平均,记为J(θ)函数,表示所有训练数据预测值与实际类别的偏差。
        (3)显然,J(θ)函数的值越小表示预测函数越准确(即h函数越准确),所以这一步需要做的是找到J(θ)函数的最小值。找函数的最小值有不同的方法,Logistic Regression实现时有的是梯度下降法(Gradient Descent)。

逻辑回归算法公式为:

 假设有N个样本,样本标签只有0和1两类可以用极大似然估计法估计模型参数,从而得到逻辑回归模型。设yi=1的概率为pi, yi=0的概率为1-pi,那么观测的概率为:

 概率由逻辑回归的公式求解,那么带进去得到极大似然函数:

最终导数变为:

逻辑回归具有以下优点:

1.形式简单,模型的可解释性非常好。从特征的权重可以看到不同的特征对最后结果的影响,某个特征的权重值比较高,那么这个特征最后对结果的影响会比较大。
 2.模型效果好。在工程上是可以接受的,如果特征工程做的好,效果不会太差,并且特征工程可以并行开发,大大加快开发的速度。
3.训练速度较快。分类的时候,计算量仅仅只和特征的数目相关。并且逻辑回归的分布式优化sgd发展比较成熟,训练的速度可以通过堆机器进一步提高,这样我们可以在短时间内迭代好几个版本的模型。
4.资源占用小,尤其是内存。因为只需要存储各个维度的特征值。
5.方便输出结果调整。逻辑回归可以很方便的得到最后的分类结果。

2.4 KNN算法

KNN最邻近分类算法的实现原理为:为了判断未知样本的类别,以所有已知类别的样本作为参照,计算未知样本与所有已知样本的距离,从中选取与未知样本距离最近的K个已知样本,根据少数服从多数的投票法则(majority-voting),将未知样本与K个最邻近样本中所属类别占比较多的归为一类。

 KNN算法的关键:
1.样本的所有特征都要做可比较的量化,若是样本特征中存在非数值的类型,必须采取手段将其量化为数值

2.样本特征要做归一化处理,样本有多个参数,每一个参数都有自己的定义域和取值范围,他们对距离计算的影响不一样,如取值较大的影响力会盖过取值较小的参数。所以样本参数必须做一些 scale 处理,最简单的方式就是所有特征的数值都采取归一化处置。
3.需要一个距离函数以计算两个样本之间的距离,通常使用的距离函数有:欧氏距离、余弦距离、汉明距离、曼哈顿距离等,一般选欧氏距离作为距离度量,但是这是只适用于连续变量。在文本分类这种非连续变量情况下,汉明距离可以用来作为度量。通常情况下,如果运用一些特殊的算法来计算度量的话,K近邻分类精度可显著提高,如运用大边缘最近邻法或者近邻成分分析法。

2.5 神经网络算法(Neural Network)

神经网络模型是模拟人类神经网络工作原理进行自我学习和演化的一种数据工作方法。神经网络在系统辨识、模式识别、智能控制等领域应用广泛,尤其在智能控制中被视为解决自动控制中控制器适应能力这个难题的关键钥匙之一。
神经网络理论是巨量信息并行处理和大规模平行计算的基础,是高度非线性动力学系统,又是自适应组织系统,可用来描述认知、决策及控制的智能行 为.它的中心问题是智能的认知和模拟,更重要的是它具有“认知”、“意识”和“感情”等高级大脑功能。它再一次拓展了计算概念的内涵,使神经计算、进化计算成为新的学科,神经网络的软件模拟得到了广泛的应用。

2.6 XGBoost算法

XGBoost是2014年2月诞生的专注于梯度提升算法的机器学习函数库,此函数库因其优良的学习效果以及高效的训练速度而获得广泛的关注。XGBoost不仅学习效果很好,而且速度也很快,相比梯度提升算法在另一个常用机器学习库scikit-learn中的实现,XGBoost的性能经常有十倍以上的提升。XGBoost算法思想就是不断地添加树,不断地进行特征分裂来生长一棵树,每次添加一个树,其实是学习一个新函数,去拟合上次预测的残差。当我们训练完成得到k棵树,我们要预测一个样本的分数,其实就是根据这个样本的特征,在每棵树中会落到对应的一个叶子节点,每个叶子节点就对应一个分数,最后只需要将每棵树对应的分数加起来就是该样本的预测值。

第三章 项目设计与实现

3.1 项目总体设计

在本次系统的开发过程中,首先要先获取到源数据集,根据数据集中的信息对数据进行清洗与降维处理,保证数据的完整性与一致性;在完成数据集的处理后,就可以在PyCharm中设计机器学习算法模型了,本着对患者负责的心态以及保证分析结果的准确性的目的,在系统中设计了多个算法同时进行预测,并进行多维分析验证;最后设计用户主界面,使用户可以在此界面输入个人身体信息,系统会将信息传递到后台的算法模型当中进行分析,最后给出预测结论。

图3.1-1:项目总体设计

3.2 数据获取

在本次系统设计中采用的数据集来自阿里天池数据集,数据来源:https://tianchi.aliyun.com/dataset/dataDetail?dataId=88343,这个数据集的原始数据来自国家糖尿病消化和肾病机构。数据集的目的是基于数据集中确定的诊断测量指标来预测一个患者是否患有糖尿病。在从更大的数据库中选择这些实例时受到了一些限制。特别是,所有收录于数据集的患者都是至少21周岁的皮马印第安女性。数据集包括多个医学预测变量和一个目标变量。预测变量包括患者的怀孕次数,她们的BMI指数,胰岛素水平,年龄等。

3.3 数据清洗

数据清洗概念就是去重,检查数据一致性,处理无效值和缺失值等,删除重复信息,纠正存在的错误。就是缺失值进行清洗、确定缺失值范围(对每个字段都计算其缺失值比例,然后按照缺失值比例和字段重要性,分别制定策略)、去除不需要的字段(清洗每做一步时都备份一下,或者在小规模数据上试验成功再处理全量数据)填充缺失内容(以业务知识或经验推测填充缺失值、以同一指标的计算结果(均值,中位数,众数等)填充缺失值)、重新取数(如果某些指标非常重要又缺失率高,需要和数据获取人员了解,是否有其他渠道取到相关数据)。

首先将目标数据集导入到程序中,查看数据的基本情况,这里采用了Pandas中的read.csv()方法将数据导入,并打印出数据所有记录。在查看的过程中发现Glucose、BloodPressure、SkinThickness、Insulin、BMI这5个维度存在缺失现象,对于数据的缺失情况,程序中采用了中值补全的方式,将数据的缺失值补全。

图3.3-1:缺失值补全代码展示 

在完成补全操作以后,程序中还引入了sklearn中的StandardScaler库对数据进行数据标准化操作,最后将文件保存到电脑上,至此完成数据清洗操作。

 图3.3-2:数据标准化代码设计

3.4 机器学习算法实现

首先,根据所要根据前面选择的算法挑选第三方库,在这里先要导入numpy、pandas等基础库作为基础支撑,其次随机森林、决策树、逻辑回归、KNN、神经网络算法需要在sklearn中进行引入操作,对于xgboost算法模型则需要单独安装和引入。

3.4.1 随机森林算法实现

在随机森林算法的实现过程中首先算法所需的变量进行赋值,并划分训练集与测试集。由于数据集较小,共有768条记录,所以在这里选取了738条记录作为训练集,30条记录作为测试集。在拿到训练集后需要调用RandomForestClassifier()随机森林分类器对数据进行训练,系统在完成训练后会导入测试集对算法模型进行测试,输出随机森林算法的算法准确率以及对算法预测结构与真实结果之间的对比,给出算法评分,具体代码如图3.4.1-1所示。

图3.4.1-1:随机森林算法代码展示 

代码运行后可以看到随机森林算法的准确率在90%左右,具有较高的准确性,应用于糖尿病预警分析中具有良好效果。

图3.4.1-2:随机森林算法结构展示 

3.4.2 决策树算法实现

决策树的代码实现于前面随机森林的设计过程类似,不同之处在于算法训练中使用了DecisionTreeClassifier()决策树分类器,其余部分大同小异本文不再过多赘述,将中心集中于算法所实现的结果展示,决策树代码如图3.4.2-1所示。

图3.4.2-1:决策树代码展示 

对于决策树算法最终得分为0.77,从整体上来看算法准确率偏低,在糖尿病的研究中并不适用。

图3.4.2-2:决策树算法实现 

3.4.3逻辑回归算法实现

就逻辑回归算法的实现结果来看,算法准确率达到了0.93,在算法评级中属于较高的水平,在诊断预测上可以作为重要的参考依据。

图3.4.3-1:逻辑回归算法实现 

3.4.4 KNN算法实现

在KNN算法对数据集的预测分析中,由于无法在一开始就确定K取何值时准确率最高,所以在程序中采用了循环遍历的方式寻找K值,共遍历14次(K值取1-15),如图3.4.4-1所示。

图3.4.4-1:KNN算法程序设计 

经过测试以后可以看到KNN算法在K=1时,算法准确率为0.8,为KNN算法在训练中准确率的最小值,如图3.4.4-2所示;而当K=14时,KNN算法的准确率达到了0.9,为KNN算法在训练中的最大值,如图3.4.4-3所示。可见KNN算法准确率受K值影响较大,需要多次进行拟合测试。

 图3.4.4.2:k=1时KNN算法准确率

 图3.4.4-3:k=14时KNN算法准确率 

3.4.5神经网络算法实现

从图3.4.5-1中可知神经网络模型的准确率为0.77,与决策树算法相差不大。

图3.4.5-1:神经网络算法实现 

3.4.6 XGBoost算法实现

对于XGBoost算法而言,其算法准确率达到了0.93,如图3.4.6-1所示。XGBoost算法在糖尿病预警分析系统的算法选择以及权重分配上无疑要居于首位。

图3.4.6-1:XGBoost算法结果展示

3.5主程序设计

根据系统需求分析,用户在打开系统界面时,首先要在主界面输入自己的身体信息,系统会检测输入信息,如果出现信息缺失或其他异常因素时,系统会给出提升并让用户重新输入信息。在接收到正确的信息后,系统会将用户信息传递到后台变量上,交给算法模型进行分析,最后给出用户的糖尿病风险情况。

图3.5-1:主程序设计 

3.6系统测试

在系统开发的最后,在程序中追加了糖尿病风险预警程序,当用户输入身体信息,后台算法模型训练结束后,风险预警程序默认为当算法模型给出算法准确率大于0.8时,则认为该用户有患上糖尿病的风险。在系统测试中,选取了源数据集中的第一条数据进行系统测试。

图3.6-1:系统测试 

 

图3.6-2:随机森林算法测试 

剩下的大家自己测吧...

结论

由于社会公众缺乏对糖尿病的认识和预防意识,他们不知道如何预防糖尿病,得了糖尿病又不能及时检查和治疗。此外,糖尿病是一种慢性终身性疾病,需要坚持长期的治疗。病因的复杂性、治疗措施的综合性和个体化都需要得到患者的主动参与,提高糖尿病患者的自觉性和主动配合,以达到良好的代谢控制,才能避免和延缓糖尿病慢性并发症的发生与发展,也可降低医疗费用。因此,提取做好糖尿病的筛查与预警尤为重要,本系统基于机器学习算法,为用户提供了一个可以自行筛查糖尿病的预警系统,可以让用户早发现,早治疗,避免诸多糖尿病以及并发症的困扰,提高每一位患者的生活质量。

数据来源:https://tianchi.aliyun.com/dataset/dataDetail?dataId=88343

一代源代码

# 首先 import 必要的模块
import numpy as np
import pandas as pd
from xgboost import XGBClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score

pd.set_option('display.max_rows', 10)
pd.set_option('display.max_columns', 10)
pd.set_option('display.width', 1000)

#   导入csv数据
df = pd.read_csv("C:/")

#   x为可能导致糖尿病的因素
x = df.drop('Outcome', axis=1)
#   y为诊断结果,1为确诊,2为未确诊
y = df['Outcome']


#   训练数据(随机森林)
def random_forest():
    #   将数据标准化
    X = df.drop('Outcome', axis=1)
    # 一共768个数据738个作为训练集,30个作为测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    # 若数值过大可以标准化,这里数值不算大,标准化后准确值较低,所以注释掉了不进行标准化
    classifier = RandomForestClassifier(criterion='entropy', n_estimators=1000, max_depth=None, min_samples_split=10,
                                        min_weight_fraction_leaf=0.02)
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    print('随机森林准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    print('模型得分::.2f'.format(classifier.score(X_test, y_test)))
    y_ = np.array(y_test)
    print('随机森林预测结果:', classifier.predict(X_test))
    print('真实结果:         ', y_)
    print('-------------------------------------------')


#   训练数据(决策树)
def decision_tree():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768)
    # 参数是经过测试得到的最高准确率的参数
    classifier = DecisionTreeClassifier(criterion='entropy', max_depth=3, min_weight_fraction_leaf=0.01)
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    print('决策树准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    print('模型得分::.2f'.format(classifier.score(X_test, y_test)))
    y_ = np.array(y_test)
    print('决策树预测结果:', classifier.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


#   训练数据(逻辑回归)
def logistic_regression():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    lr = LogisticRegression(random_state=0, max_iter=1000)
    lr.fit(X_train, y_train)
    y_pred = lr.predict(X_test)
    print('逻辑回归准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    print('模型得分::.2f'.format(lr.score(X_test, y_test)))
    y_ = np.array(y_test)
    print('逻辑回归预测结果:', lr.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


#   训练数据(KNN)寻找最佳的k值
def k_nn():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    error = []
    # 由于一开始并不清楚K取多少准确率最高,所以写了一个K为1-14的for循环,通过检查误差值来判断最合适的K值
    for k in range(1, 15):
        classifier = KNeighborsClassifier(n_neighbors=k)
        classifier.fit(X_train, y_train)
        y_prediction = classifier.predict(X_test)
        error.append(np.mean(y_prediction != y_test))
        print('当k=', k, '时的准确率')
        print(confusion_matrix(y_test, y_prediction))
        print(classification_report(y_test, y_prediction))
        print('模型得分::.2f'.format(classifier.score(X_test, y_test)))
        print('--------------------------------------------------------------------------------------')


def n_n():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    mlp = MLPClassifier(random_state=0, max_iter=5000)
    mlp.fit(X_train, y_train)
    y_pred = mlp.predict(X_test)
    print('Neural Network准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    print('模型得分::.2f'.format(mlp.score(X_test, y_test)))
    y_ = np.array(y_test)
    print('Neural Network预测结果:', mlp.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


def xgb():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    xgb = XGBClassifier(gamma=0, use_label_encoder=False, learning_rate=0.01, max_depth=10, n_estimators=10000, random_state=34, reg_lambda=6, reg_alpha=3, verbosity=0)
    xgb.fit(X_train, y_train)
    y_prediction = xgb.predict(X_test)
    print('XGBoost准确率')
    print(confusion_matrix(y_test, y_prediction))
    print(classification_report(y_test, y_prediction))
    print(accuracy_score(y_test, y_prediction))
    print('模型得分::.2f'.format(xgb.score(X_test, y_test)))
    y_ = np.array(y_test)
    print('XGBoost预测结果:', xgb.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


#   主菜单
def main_menu():
    print('-' * 11, '糖尿病诊断预测', '-' * 11)
    print('|:20|:20|'.format('1.输入指标', '2.退出系统'))


#   选择模型菜单
def option_menu():
    print('-' * 11, '训练模型选择', '-' * 11)
    print('|:20|:20|'.format('1.所有模型', '2.随机森林'))
    print('|:20|:20|'.format('3.决策树', '4.逻辑回归'))
    print('|:23|:20|'.format('5.KNN', '6.连接模型'))
    print('|:23|'.format('7.XGBoost'))


#   登出
def logout():
    print('感谢使用')

if __name__ == '__main__':
    while True:
        main_menu()
        key = input('请输入你的选择: ')
        if key == '1':
            try:
                Pregnancy = float(input('输入怀孕指数:'))
                Glu = float(input('输入葡萄糖指数:'))
                BP = float(input('输入血压:'))
                ST = float(input('输入皮肤厚度:'))
                Ins = float(input('输入胰岛素指数:'))
                BMI = float(input('输入体质指数:'))
                DBF = float(input('输入糖尿病谱系功能指数:'))
                Age = float(input('输入年龄:'))
            except:
                print("不符合输入要求,请重新输入")
                continue
			# 将输入的值放在一个列表中
            indices = [Pregnancy, Glu, BP, ST, Ins, BMI, DBF, Age]
            l = []
            for index in indices:
                l.append(index)

            empty_indices = "Pregnancies": l[0], "Glucose": l[1], "BloodPressure": l[2], "SkinThickness": l[3], "Insulin": l[4], "BMI": l[5], "DiabetesPedigreeFunction": l[6], "Age": l[7]
            data = pd.DataFrame(empty_indices, index=[0])
            data = data.set_index('Pregnancies')
            data.to_csv("new.csv")
            df_1 = pd.read_csv("new.csv")
            s = df_1.iloc[0]
            # 选择模型菜单
            option_menu()
            option = input('请选择模型:')
            #   显示所有的训练模型结果
            if option == '1':
                random_forest()
                decision_tree()
                logistic_regression()
                k_nn()
                n_n()
                xgb()
            elif option == '2':
                random_forest()
            elif option == '3':
                decision_tree()
            elif option == '4':
                logistic_regression()
            elif option == '5':
                k_nn()
            elif option == '6':
                n_n()
            elif option == '7':
                xgb()
            else:
                print('无效输入')
                continue
        elif key == '2':
            logout()
            break
        else:
            print('无效输入')

改进源代码

import numpy as np
import pandas as pd
from xgboost import XGBClassifier
from sklearn.tree import DecisionTreeClassifier
from sklearn.neural_network import MLPClassifier
from sklearn.neighbors import KNeighborsClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier
from sklearn.model_selection import train_test_split
from sklearn.metrics import classification_report, confusion_matrix, accuracy_score

pd.set_option('display.max_rows', 10)
pd.set_option('display.max_columns', 10)
pd.set_option('display.width', 1000)

#   导入csv数据
df = pd.read_csv("C:/")

#   x为可能导致糖尿病的因素
x = df.drop('Outcome', axis=1)
#   y为诊断结果,1为确诊,2为未确诊
y = df['Outcome']


#   训练数据(随机森林)
def random_forest():
    #   将数据标准化
    X = df.drop('Outcome', axis=1)
    # 一共768个数据738个作为训练集,30个作为测试集
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    # 若数值过大可以标准化,这里数值不算大,标准化后准确值较低,所以注释掉了不进行标准化
    classifier = RandomForestClassifier(criterion='entropy', n_estimators=1000, max_depth=None, min_samples_split=10,
                                        min_weight_fraction_leaf=0.02)
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    print('随机森林准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    a = format(classifier.score(X_test, y_test))
    print(a)
    if float(a) >= 0.8:
        print("有糖尿病风险")
    else:
        print("无糖尿病风险")
    y_ = np.array(y_test)
    print('随机森林预测结果:', classifier.predict(X_test))
    print('真实结果:         ', y_)
    print('-------------------------------------------')


#   训练数据(决策树)
def decision_tree():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768)
    # 参数是经过测试得到的最高准确率的参数
    classifier = DecisionTreeClassifier(criterion='entropy', max_depth=3, min_weight_fraction_leaf=0.01)
    classifier.fit(X_train, y_train)
    y_pred = classifier.predict(X_test)
    print('决策树准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    b = format(classifier.score(X_test, y_test))
    print(b)
    if float(b) >= 0.8:
        print("有糖尿病风险")
    else:
        print("无糖尿病风险")
    print(format(classifier.score(X_test, y_test) < 0.8))
    y_ = np.array(y_test)
    print('决策树预测结果:', classifier.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


#   训练数据(逻辑回归)
def logistic_regression():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    lr = LogisticRegression(random_state=0, max_iter=1000)
    lr.fit(X_train, y_train)
    y_pred = lr.predict(X_test)
    print('逻辑回归准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    c = format(lr.score(X_test, y_test))
    print(c)
    if float(c) >= 0.8:
        print("有糖尿病风险")
    else:
        print("无糖尿病风险")
    y_ = np.array(y_test)
    print('逻辑回归预测结果:', lr.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


#   训练数据(KNN)寻找最佳的k值
def k_nn():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    error = []
    # 由于一开始并不清楚K取多少准确率最高,所以写了一个K为1-14的for循环,通过检查误差值来判断最合适的K值
    for k in range(1, 15):
        classifier = KNeighborsClassifier(n_neighbors=k)
        classifier.fit(X_train, y_train)
        y_prediction = classifier.predict(X_test)
        error.append(np.mean(y_prediction != y_test))
        print('当k=', k, '时的准确率')
        print(confusion_matrix(y_test, y_prediction))
        print(classification_report(y_test, y_prediction))
        d = format(classifier.score(X_test, y_test))
        print(d)
        if float(d) >= 0.8:
            print("有糖尿病风险")
        else:
            print("无糖尿病风险")

#   训练数据(Neural Networks)
def n_n():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    mlp = MLPClassifier(random_state=0, max_iter=5000)
    mlp.fit(X_train, y_train)
    y_pred = mlp.predict(X_test)
    print('Neural Network准确率')
    print(confusion_matrix(y_test, y_pred))
    print(classification_report(y_test, y_pred))
    print(accuracy_score(y_test, y_pred))
    e = format(mlp.score(X_test, y_test))
    print(e)
    if float(e) >= 0.8:
        print("有糖尿病风险")
    else:
        print("无糖尿病风险")
    y_ = np.array(y_test)
    print('Neural Network预测结果:', mlp.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


  #训练数据(XGBoost)
def xgb():
    X = df.drop('Outcome', axis=1)
    X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=30 / 768, random_state=0)
    xgb = XGBClassifier(gamma=0, use_label_encoder=False, learning_rate=0.01, max_depth=10, n_estimators=10000, random_state=34, reg_lambda=6, reg_alpha=3, verbosity=0)
    xgb.fit(X_train, y_train)
    y_prediction = xgb.predict(X_test)
    print('XGBoost准确率')
    print(confusion_matrix(y_test, y_prediction))
    print(classification_report(y_test, y_prediction))
    print(accuracy_score(y_test, y_prediction))
    f = format(xgb.score(X_test, y_test))
    print(f)
    if float(f) >= 0.8:
        print("有糖尿病风险")
    else:
        print("无糖尿病风险")
    y_ = np.array(y_test)
    print('XGBoost预测结果:', xgb.predict(X_test))
    print('真实结果:         ', y_)
    print('--------------------------------------------------------------------------------------')


#   主菜单
def main_menu():
    print('-' * 11, '糖尿病诊断预测', '-' * 11)
    print('|:20|:20|'.format('1.输入指标', '2.退出系统'))


#   选择模型菜单
def option_menu():
    print('-' * 11, '训练模型选择', '-' * 11)
    print('|:20|:20|'.format('1.所有模型', '2.随机森林'))
    print('|:20|:20|'.format('3.决策树', '4.逻辑回归'))
    print('|:23|:20|'.format('5.KNN', '6.连接模型'))
    print('|:23|'.format('7.XGBoost'))


#   登出
def logout():
    print('感谢使用')

if __name__ == '__main__':
    while True:
        main_menu()
        key = input('请输入你的选择: ')
        if key == '1':
            try:
                Pregnancy = float(input('输入怀孕指数:'))
                Glu = float(input('输入葡萄糖指数:'))
                BP = float(input('输入血压:'))
                ST = float(input('输入皮肤厚度:'))
                Ins = float(input('输入胰岛素指数:'))
                BMI = float(input('输入体质指数:'))
                DBF = float(input('输入糖尿病谱系功能指数:'))
                Age = float(input('输入年龄:'))
            except:
                print("不符合输入要求,请重新输入")
                continue
			# 将输入的值放在一个列表中
            indices = [Pregnancy, Glu, BP, ST, Ins, BMI, DBF, Age]
            l = []
            for index in indices:
                l.append(index)

            empty_indices = "Pregnancies": l[0], "Glucose": l[1], "BloodPressure": l[2], "SkinThickness": l[3], "Insulin": l[4], "BMI": l[5], "DiabetesPedigreeFunction": l[6], "Age": l[7]
            data = pd.DataFrame(empty_indices, index=[0])
            data = data.set_index('Pregnancies')
            data.to_csv("new.csv")
            df_1 = pd.read_csv("new.csv")
            s = df_1.iloc[0]
            # 选择模型菜单
            option_menu()
            option = input('请选择模型:')
            #   显示所有的训练模型结果
            if option == '1':
                random_forest()
                decision_tree()
                logistic_regression()
                k_nn()
                n_n()
                xgb()
            elif option == '2':
                random_forest()
            elif option == '3':
                decision_tree()
            elif option == '4':
                logistic_regression()
            elif option == '5':
                k_nn()
            elif option == '6':
                n_n()
            elif option == '7':
                xgb()
            else:
                print('无效输入')
                continue
        elif key == '2':
            logout()
            break
        else:
            print('无效输入')

数据清洗

import numpy as np
import pandas as pd

train = pd.read_csv("C:/Users/Administrator/Desktop/diabetes.csv")
print(train.head())

#查看缺失值较多的数据统计
NaN_col_names = ['Glucose','BloodPressure','SkinThickness','Insulin','BMI']
train[NaN_col_names] = train[NaN_col_names].replace(0, np.NaN)
print(train.isnull().sum())


#中值补充缺实值
medians = train.median()
train = train.fillna(medians)

print(train.isnull().sum())


#  get labels
y_train = train['Outcome']
X_train = train.drop(["Outcome"], axis=1)

#用于保存特征工程之后的结果
feat_names = X_train.columns

# 数据标准化
from sklearn.preprocessing import StandardScaler

# 初始化特征的标准化器
ss_X = StandardScaler()

# 分别对训练和测试数据的特征进行标准化处理
X_train = ss_X.fit_transform(X_train)

#存为csv格式
X_train = pd.DataFrame(columns = feat_names, data = X_train)

train = pd.concat([X_train, y_train], axis = 1)

train.to_csv('C:/Users/Administrator/Desktop/pima-diabetes.csv',index = False,header=True)

print(train.head())

看到这里就点个赞吧(@-@)

基于机器学习模型的糖尿病带病人群医疗险风险保费测算

点击上方 “中国保险学会”可以订阅!

本文字数:9858字

阅读时间:20分钟

基于机器学习模型的糖尿病带病人群医疗险风险保费测算
基于机器学习模型的糖尿病带病人群医疗险风险保费测算

文章来源:《保险研究》2020年第11期

一、引言

我国商业健康险发展正经历从量变到质变的关键时期。在此过程中,健康险产品不断创新,健康险定价也受到产品创新和大数据等技术影响并发生相应的变化。陈滔、卓志等(2002)详细介绍了商业医疗保险保费的计算原则、定价因素和保费制度,对定价所需的统计数据进行了细致的分类和整理,并总结了商业医疗险定价的基本步骤。谢远涛、李政宵(2019)从费率厘定、准备金计提、健康管理与基金分配方面对健康保险精算研究的主要框架和主要研究模型作出了详尽的总结和归纳。马绍东、陈滔(2011)从健康、失能、死亡三状态模型出发,分析了国外失能收入损失保险的定价方法——挪威法和曼联法,提出了使用发生频数/暴露数新方法估计转移概率,并给出平滑转移概率的GLM模型。仇春涓(2014)等综合使用了四类人群的保险赔付信息,采用GLM模型做了实证分析,结果证明医疗险的费率结构在性别、年龄的基础上增加婚姻状况、地区等其他风险因素可以有效缓解费率的不公平性;在理赔成本假设中加上婚姻状况能显著降低部分人群住院医疗保险的保费。


随后,学者们考虑了大数据的影响,并将视角转向非标准体。薛付忠(2017)研究了大数据背景下疾病风险的评估, 构建了疾病预测模型的建模技术规程,提出了大数据驱动的健康保险与健康维护一体化模式,创建了“大数据背景下整合健康保险&健康维护的理论方法体系”。这对健康保险精算定价具有重要的指导作用。张宁(2015)针对非标准体中的“糖尿病”等群体进行了长寿风险的度量,计算了对应的发病率及其趋势。糖尿病(DiabetesMellitus, DM)是由胰岛素分泌障碍、胰岛素抵抗等引发的糖、蛋白质、脂肪、水和电解质等一系列代谢紊乱综合征(Alberti. K .G, 1998)。研究表明,全球20~79岁的糖尿病患者人数将从2015年的4.15亿(每11名成年人中有1名糖尿病患者)增加到2040年的6.42亿(每10名成年人中有1名糖尿病患者)(International Diabetes  Federation,2015)。2017年,世界卫生组织宣布,全球每年约有400万人死于糖尿病,占所有死亡人数的10.7%( National Health and Family Planning Commission of PRC.,2017),糖尿病已经成为第15位致命疾病因素(GBD 2015 Mortality and Causes of Death Collaborators,2016)。糖尿病导致的长期医疗费用、劳动力丧失、过早死亡及生活质量降低等直接和间接成本带来了沉重的社会经济负担(American Diabetes Association,2013)。2015年,全球糖尿病医疗费用达到6730亿美元(International Diabetes Federation,2015),而全球糖尿病相关的支出达到1.3万亿美元,预计2030年将达到2.2万亿美元,占全球GDP的比例将从1.8%上升至2.2%(Bommer,et al.,2018)。


近年来,中国糖尿病发病率持续升高,目前是世界上发病率最高的国家(Mendis Shanthi,2015)。同时,中国糖尿病患病率也居高不下,2017年中国大陆糖尿病总体患病率为12.8%,糖尿病前驱期患病率为35.2%( Yongze Li,2020 )。2015年,中国糖尿病患者总数增加至1.1亿人左右,预计到2040年将达到1.51亿( Guariguata L,2014)。据估计,中国有1.139亿成年人患有糖尿病,占全球糖尿病患者的24%。作为目前世界上糖尿病患者最多的国家,2017年中国与糖尿病相关的医疗保健费用为1100亿美元(按购买力平价)(International Diabetes Federation,2017)。糖尿病也为个人生活带来了极大的经济负担。研究表明,2013-2015年中国每名糖尿病住院患者年均住院费用为1039.72美元,医保基金支付总额和个人现金卫生支出总额分别为748.11美元和291.62美元( Zhang X,2020),每名糖尿病门诊患者年均门诊费用为141.6美元,同时药品费用分别占有/无糖尿病患者医疗费用的51.8%和79.7%( Haibin Wu,2019)。


由于糖尿病发病机制复杂,病程较长且并发症较多,因此国家将糖尿病纳入慢病管理,开设基层慢病门诊,同时将糖尿病常用药物纳入基本医保目录,降低患者疾病经济负担(提高慢病保障水平,促进慢病健康管理)。但由于糖尿病患者人群基数较大,各地区医疗资源分布不均,因此对糖尿病患者的管理力度无法保障(刘霞等,2016),由此出现商业健康保险发展机遇。研究表明,商业健康险对于扩大糖尿病治疗覆盖范围、完善常规筛查机制和管理、纳入新疗法、新技术、新药品以降低糖尿病患者误诊率、失诊率乃至控制糖尿病前驱期发展起到重要作用(Sean Mahoney,2020)。自2013年第一款糖尿病专属健康险产品问世至今,已有多家商业保险公司推出糖尿病健康险产品,进军这一细分蓝海市场。这些产品成为对带病投保的积极尝试,针对糖尿病及其相关并发症的医疗保障和健康管理相结合是其最大亮点(糖尿病人可购买专属健康险)。但这些产品也广泛存在定价保守、保障时间短等问题,糖尿病健康险在精准定价和差异化定价方面有待深耕。本文将基于传统方法的思路,在机器学习模型预测住院费用的基础上(赵颖旭等,2020),引入住院率模型,对非标准体中的“糖尿病”群体进行费用预测和保费测算。


本文第二部分主要介绍模型方法;第三部分介绍本文研究的数据来源和数据统计特征;第四部分是住院费用机器学习测算及相应结果;第五部分为保费测算及结果;第六部分是结论。

二、模型与方法

(一)保费测算模型

基于机器学习模型的糖尿病带病人群医疗险风险保费测算

COB为社保等其他保障支付所占总费用比例,d为免赔额,c为赔付比例,E(N)表示一定时间内的平均索赔次数,其根据保险条款不同而有不同的估计方法,在本文中,考虑触发索赔的是住院,所以在实际计算中用住院率代替。同样的原因,考虑到实际触发索赔的住院率因素,上述公式是在计算期望后扣减免赔额,而非先减掉免赔额再计算期望。


(二)E(X)的测算


E(X)测算最常用的是广义线性模型。孟生旺(2007)简要分析了传统的非寿险定价的缺点,并将广义线性模型具体应用到车险定价。王新军、王亚娟(2013)构建了索赔次数和索赔强度的广义线性模型,并针对模型系数不显著的情况采用了合并风险等级的方法。机器学习模型由于不需要依赖于损失分布的假设,可以避免广义线性模型的缺陷,提高模型的精度,并且更方便处理大数据集,被广泛应用到金融、保险领域。孟生旺、黄一凡(2018)将随机森林模型、神经网络模型和XGBoost模型应用到出险概率预测,并将XGBoost中的特征重要性作为定价、理赔的重要参考。孟生旺、李天博等(2017)基于支持向量机、神经网络和集成学习等机器学习算法对车险索赔频率和累计赔款建立预测模型,并对不均衡的数据进行样本调整,结果显示机器学习模型优于传统的模型。本文将延续上述思路,并将更多机器学习模型引入到费用测算中,这些机器学习模型包括Lasso回归、XGBoost、LightGBM和随机森林。


1. Lasso回归


相较于传统的定价方法,GLM虽然在拟合效果上很大优势,但是仍然存在一些问题,比如自变量之间存在多重共线性、过拟合现象,而Lasso回归在目标函数中通过添加正则项(L1范式)防止过拟合,同时可以使某些变量的回归系数为零,避免线性回归变量之间的共线性,并且通过减少特征的数量,从而得到筛选特征的作用。目前,Lasso回归已广泛应用在医药卫生领域,用于卫生统计(陶春海、王梦颖,2017)、住院费用的估算(韩耀风,2017;Huang,2020;李阳,2020)等。


Lasso回归的目标函数为: 

基于机器学习模型的糖尿病带病人群医疗险风险保费测算

2.XGBoost


XGBoost是Chen和Guestrin(2016) 提出的一种集成学习模型,是GBDT算法(Gradient Boosting Decision Tree,GBDT)的工程实现。GBDT在模型训练时只使用了损失函数的一阶导数,而XGBoost对损失函数进行二阶泰勒展开,同时使用一阶和二阶导数,并加入了正则项来防止过拟合,从而进一步提高了模型泛化能力。


3.LightGBM


LightGBM算法属于boosting集成方法,它使用回归树作为基学习器,具体特点有:算法速度快,内训使用得到了极大的优化;并行学习;算法使用直方图算法将特征值分桶,寻找决策树的最佳分支点,在确定最优划分属性的时候,比GBDT算法“遍历所有可能划分点计算信息增益”速度快得多;采用了梯度单边采样技术和独立特征合并技术。LightGBM与XGBoost不同,它采用的是更高效率、带有深度限制的叶子生长算法—leafwise,而非按层生长的决策树生长策略—levelwise;寻找最优的分支点的时候,lightGBM会遍历所有的叶节点,寻找最大的信息增益进行分支,使得速度得到很大提升。


4.随机森林


随机森林以决策树为基学习器并引入随机选择的属性,是集成学习bagging的一个扩展变体,具有简单、容易实现,计算开销小等特点。在传统的决策树中,在判定划分时是在当前节点的所有属性中选择最优属性,而随机森林是在该节点的所有属性中随机抽取一个子集,然后在这个子集中选择一个最优的划分属性。随机森林在训练模型时是基于自助采样法(bootstrap sampling),初始训练集约有63.3%的样本出现在采样集中。随机森林基学习器受到样本扰动和数据特征的扰动,具有更强的多样性,最终的泛化性能得到增强。实际上,随机森林中的两个随机性实际上减少了森林估计量方差:单个决策树通常表现出较高的方差且易出现过度拟合现象,而森林中注入的随机性产生决策树,其预测误差可以互相解耦,通过取这些预测的平均值,其结果可以消除一些误差。


(三)住院率模型


住院费用需要结合住院率才能进行保费测算。这里住院率实际上是全部人群因为特定疾病住院的概率,需要区分糖尿病主要诊断住院和糖尿病其他诊断住院。很显然,两者住院率小于等于发病率,且有下式

基于机器学习模型的糖尿病带病人群医疗险风险保费测算

发病率的数据和估计方法采用张宁(2015)的数据和方法。该方法将Lee-Carter模型引入到发病率的预测和分析中,并以1995-2010年的数据得到了发病率趋势。Lee-Carter模型是一种双线性模型,它原来用于对死亡率趋势的预测:

基于机器学习模型的糖尿病带病人群医疗险风险保费测算

三、数据统计描述与测算指标

国际疾病分类 (International Classification of Diseases,ICD)是一种对不同类型的疾病及与健康相关问题进行编码和分类的国际标准,自产生至今已有上百年的历史,是统计居民健康状况的国际分类标准。根据世界卫生组织和我国卫健委要求,我国自1987年起推广应用ICD-9(ICD第9次修订本),从2002年起改为使用ICD-10(ICD第10次修订本)并一直沿用至今(贾友波、宋宪锟,2020;吕国友等,2019)。本研究对糖尿病患者的定义为:主要诊断或其他诊断ICD-10编码为表1的住院患者(牛犇,2017;王莹等,2006)。


本研究选取2015-2017年来自全国30个省、自治区和直辖市的主要诊断或者其他诊断包含表1中诊断编码的住院病例共计3835960条,数据的特征有299个,可大致分为两类:疾病属性和客户属性。初始数据需要经过预处理才能够训练模型。本文对初始数据进行了缺失值删除和异常值处理,具体做法是删除医疗费用大于99%分位数和小于1%分位数的部分。预处理后的数据特征的具体分类信息如表2所示。


疾病属性变量主要是指疾病类型、并发症和合并症。疾病类型作为分类变量的取值主要有I型、II型糖尿病,并发症共有8种。病人可以同时罹患多种并发症和合并症。客户属性是客户本人的信息,共分为5大类,除年龄外其余均为分类变量。


如表1所示:糖尿病诊断类型主要有I型糖尿病、II型糖尿病;并发症类型主要有I型糖尿病并发症、II型糖尿病并发症:视网膜、肾、脑神经疾病、周围血管疾病、心血管疾病(ICD10:I20-I25)、脑血管疾病(ICD10:I60-I67,I69)及其他并发症;合并症有高血压、高血脂、结核病、免疫接种和筛查性传染病、急性心肌梗死、传导障碍等269类。社保类型有城镇职工基本医疗保险、城镇居民基本医疗保险、新型农村合作医疗、贫困救助、商业医疗保险、全公费、全自费、其他社会保险、其他。婚姻状况有未婚、已婚、丧偶、离婚、其他。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


对费用预测变量进行分析,如表2所示。罹患心血管和脑血管并发症的比例较高,二者之和达48.59%,且导致平均费用增加;男性人数比例比女性高16.83%,同时人均费用高于女性13.56%;华东和华北两地所占比重较大,共计达到47.17%,华北地区人均费用最高,高达18352.22元,比第二位华南地区高出25.90%;城镇职工基本医疗保险、新型农村合作医疗和城镇居民基本医疗保险覆盖面最广,三者占比之和达到75.70%,且这三者的人均费用也最少,人均费用最高的是全公费,比最低的新型农村合作医疗高出116.86%。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


图1和表3是经过处理后的费用损失分布图和统计特征描述。结果表明,费用分布的偏度为2.74,峰度为8.47,费用数据整体呈右偏分布,而且尾部较厚。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


如表4所示,本文数据涵盖2015-2017三年数据,且每年样本数呈上升趋势,2017年样本量占样本总量比例达49.05%。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


图2显示,住院人群主要集中在40~90岁,其中0~20岁的住院人群比例仅为1.36%,而40~90岁的住院人群高达93.87%。图3人均费用的图像说明:80岁以后费用的增长率要远大于80岁以前;除在幼儿阶段出现一小段年龄与人均费用反向关系,大致也可看出年龄与费用呈正相关;且人均费用在80岁以后呈快速上升的趋势,说明在步入高龄阶段后,医疗费用的风险会大幅增加。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算

四、住院费用的机器学习

预测实践与结果

(一)模型拟合比较


模型运算的最终结果如表5所示,机器学习算法的RMSE和MAE在15000和10000以下,R2都大于0.1,而传统的广义线性模型无论是在模型的拟合优度还是平均误差指标上都要差于机器学习模型。机器学习模型中LightGBM模型的预测效果是最突出的:一方面,核心原因在于LightGBM基于回归树方法,回归树在医疗数据分析中本身具有较好的效果和较优的可解释性(赵颖旭等,2020);另一方面,所使用的医疗数据作为抽样数据,其原始分布未知,LightGBM本身对此类问题具有很好的效果(Guolin Ke,2017)。


图4是LightGBM特征重要性前50个排序图,排在前面的变量不仅有年龄、性别、省份等一些客户属性,还有心血管和脑血管等并发症以及高血压、其他肝脏疾病、高血脂等合并症,说明并发症和合并症对最终的费用会产生一定的影响。从图中还可以看到,投保时间在所有因素中排序最靠前,充分反映了对应疾病住院率随年龄增长的直接趋势,与医疗险和重疾险整体趋势一致。 

基于机器学习模型的糖尿病带病人群医疗险风险保费测算
基于机器学习模型的糖尿病带病人群医疗险风险保费测算


(二)机器学习模型费用预测


本文使用拟合效果较好的LightGBM模型,该机器学习模型对患者住院费用的预测结果见图5、图6。这与既往研究关于老年患者住院费用变化趋势和结构的结论是一致的(严敬琴,2019;黄茂娟等,2017;郑金坡等,2017)。


以北京市为例,个体的住院费用结果如图5、图6所示。从图中可以看出,年龄区间、性别、糖尿病诊断类型、并发症和合并症对最终的住院费用都有重要的影响。整体来看,在45~64岁之间,男性的住院费用高于女性,但是65岁后女性的住院费用略高于男性,次要诊断的住院费用高于主要诊断的住院费用。并发症会增加个体的住院费用,尤其是心血管疾病对住院费用影响最大。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


五、保费测算与分析

仅通过费用预测结果不能得到保费与各风险因素的关系,保费的计算不仅受住院费用的影响,而且还取决于住院率、社保、免赔额和自付比例等多种因素。基于公式1和公式2,本文测算了不同因素对保费的影响。图7~图10展示了不同年龄区间、性别、并发症类型、省份对应的平均住院率。图7表明随着个体年龄的增大,主要诊断的住院率有下降的趋势,次要诊断的住院率有上升的趋势。图8展示出的性别的平均住院率几乎不存在差异。图9则体现出了不同并发症类型对住院率的影响,结果显示II型糖尿病的周围血管并发症、肾脏相关的并发症、视网膜并发症会导致更高的次要诊断的住院率。图10展现了各地区之间的住院率的差别,重庆、青海、天津三地的次要诊断的住院率最高。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


(一)基础因素的定价影响与分析


这里基础定价因素包括年龄、地区和性别等。图11给出了年龄对保费的影响。从中可以看出:年龄对保费的影响整体趋势与生命表类似,在幼儿期,年龄和保费存在一个反向变动关系,而后二者呈正向关系,而且随着年龄的增大,每增加一岁对保费对年龄的变动越敏感。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


表6是三个省的平均保费测算结果。从表中可以看出,北京市平均费用要高于辽宁和湖北,这可能是北京市的社保支付比例高于辽宁和湖北所导致,因此在最后的保费计算结果中北京市最低。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


表7是关于性别的平均保费的测算结果。从表中可以看出,在免赔额为5000元时,男性和女性的保费很相近,女性略高于男性。但是免赔额为10000元时女性保费显著大于男性保费。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


表8展示了不同社保类型的保费测算结果。新农合由于其社保支付比例较低,虽然平均费用低于城镇居民,最终的保费却高于城镇居民。城镇职工的保费最高可能是因为其平均费用高且住院率高。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


表9是不同婚姻类型的平均保费测算结果,其中未婚人群平均保费最高,离婚人群平均保费最低。这可能是因为未婚人群中未工作者占比较高、其社保支付比例水平较低,离婚人群相较于丧偶人群和已婚人群住院率较低。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


(二) 投保计划相关因素的影响与分析


表6~表9给出了免赔额为500元0或10000元和自付比例为0或10%的四种不同组合的保费测算结果。结果显示:免赔额增加和自付比例上升会使保费下降,而且免赔额越大,自付比例变动对保费的影响越大。免赔额越高意味着赔付的门槛越高,同等条件下会降低保险公司赔付费用,所以会降低个人保费。而自付比例上升,意味着个人将面临更高的费用分摊,会降低保险公司的赔付费用,保费会下降。


(三) 并发症与主要合并症因素的影响与分析


本文考虑的并发症类型为:I型无并发症、I型有并发症、II型无并发症、II型心血管并发症、II型脑血管并发症、II型神经并发症、II型周围血管并发症、II型肾并发症、II型视网膜并发症、II型其他并发症状等。本文考虑的主要合并症为:高血压、高血脂等,具体见表10。


以某省男性为例,年龄为45-64岁的并发症和合并症的平均保费测算结果如表10所示。并发症会导致保费的增加,其中II 型心血管并发症使费用增加的最大。

基于机器学习模型的糖尿病带病人群医疗险风险保费测算


(四)综合测算结果


图12~14给出了以某省为例的带有糖尿病并发症的保费综合测算结果。该结果测算的保费为精算纯保费。从该结果可以看到,对普通民众来说,其保费负担在可接受范围内,同时,考虑到免赔额的影响,保险公司可以在此基础上开发更具有竞争力和满足客户需求的产品。 

基于机器学习模型的糖尿病带病人群医疗险风险保费测算
基于机器学习模型的糖尿病带病人群医疗险风险保费测算

六、结语

本研究在医疗保险协同创新的背景下,基于覆盖范围广泛的大量样本,证实了机器学习方法较传统定价方法能够进行更准确的费用预测。借助于该费用预测,进行了考虑不同因素的“非标准体”保费测算,并给出了不同因素对保费的影响,从而支持针对非标准体的定价,以及借助医疗数据的保险创新,为保险公司开发带病投保的产品,进行保费测算提供基础。从应用视角看,借助机器学习的保费测算还可以为保险反欺诈和运营控费提供支持。例如当投保人出险后索赔的住院费用显著高于相同特征组的预测住院费用时,保险公司可进行自动筛选并重点审核。

辑:于小涵


中国保险学会

构建保险大社区

微信:iicbxzx


以上是关于Python课程设计项目-基于机器学习的糖尿病风险预警分析系统的主要内容,如果未能解决你的问题,请参考以下文章

Python 机器学习避免碰撞课程设计

Python数据挖掘课程五.线性回归知识及预测糖尿病实例

文献详解丨16+文章教你如何用机器学习挖掘2型糖尿病特征

Python课程设计大作业:利用爬虫获取NBA比赛数据并进行机器学习预测NBA比赛结果

数据分析训练-Pima印第安人数据集上的机器学习-分类算法(根据诊断措施预测糖尿病的发病)

糖尿病遗传风险检测挑战赛-权重融合