文章目录
- 综合实践一:医疗保险欺诈行为分析
- 一 分析目标
- 二 实现步骤
- 三 数据准备
- 四 特征工程
- 五 模型训练
- 六 性能度量
- 七 提交要求
- 代码块
- 三 数据准备
- 4 特征工程
- 六、模型训练和性能度量
综合实践一:医疗保险欺诈行为分析
实践项目概述
本实践项目的数据集包括:
- 投保人信息(Policy_Holder.csv)
- 医疗机构信息(Provider.csv)
- 索赔信息(Claim.csv)
索赔信息中主要为医疗机构与投保人两者进行索赔的记录。
医疗机构信息
- ProviderID:医疗机构编号
- ProviderType:医疗机构大类
- ProviderSpecialty:医疗机构细类
- Location:位置编码
投保人信息
- Policy_HolderID:投保人编号
- ProgramCode:保险条款
- MEDcode:治疗措施编码
- Age:年龄
- Sex:性别
一 分析目标
- 结合业务理解和分析,分别为投保人和医疗机构构建特征;
- 对投保人和医疗机构的行为进行特征分析;
- 通过聚类算法发现投保人和医疗机构中存在的疑似欺诈行为。
二 实现步骤
- 抽取医疗保险的历史数据;
- 对抽取的医疗保险的历史数据进行描述性统计分析,分析投保人信息和医疗机构信息;
- 采用聚类算法发现投保人和医疗机构中存在的疑似欺诈行为;
- 对疑似欺诈行为结果和聚类结果进行性能度量分析,并进行模型优化。
三 数据准备
- 对投保人信息、医疗机构信息、索赔信息进行描述性统计分析;
- 对数据集中的缺失值、重复值、异常值,以及格式与内容不规范的数据进行数据清洗;
- 绘制饼图分析保险条款种类;
- 绘制条形图分析治疗措施编码类别;
- 绘制条形图分析投保人所投保险的年龄分布情况;
- 绘制条形图分析医疗机构大类分布情况。
四 特征工程
对不同主题对象构建特征,通过分别对投保人、医疗机构的出险和索赔模式前后发生变化判断疑似医疗保险的欺诈行为。
- 结合投保人信息和索赔信息,挑选有效的投保人相关特征;
- 通过医疗机构信息和索赔信息提取有效的医疗机构特征;
- 根据投保人的住院开始时间将其划分为上半年(1Y)和下半年(2Y)两部分;
- 选取索赔订单中保费覆盖额、账单金额、支付金额特征,分别按上半年和下半年进行统计,从而得到投保人半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数;
- 将投保人信息中的年龄、性别、治疗措施编码、保险条款属性特征进行编码转换,按值展开(年龄除外);
- 组合上述特征,构成投保人特征;
- 按医疗机构编号和所属时间段分组,统计投保人数和处理过程数量;
- 选取索赔订单中保费覆盖额、账单金额、支付金额特征,分别按上半年和下半年进行统计,从而得到医疗机构半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数;
- 选取医疗机构信息中的医疗机构大类、医疗机构细类和位置编码,与上述特征(第7条和第8条)构成医疗机构特征;
- 通过编码转换将医疗机构大类、医疗机构细类转化为数值型数据。
五 模型训练
数据集中不存在真实标签,因此,采用机器学习中的无监督算法构建医疗保险的欺诈模型。通过聚类算法分别对经过特征变换后投保人和医疗机构的特征集进行分群,输出分群结果。
六 性能度量
- 将投保人聚类成 5 类类群,选取投保人数量、平均支付金额、平均支付笔数等具有代表性的指标,进行类群间的差异性比较;
- 分析投保人上半年和下半年的索赔变化情况,即投保人类群的迁移统计;
- 根据上述分析判断投保人是否疑似存在医疗保险欺诈行为,并输出投保人信息;
- 将医疗机构聚类成 5 类类群,选取医疗机构数量、平均投保人数量、平均账单金额等具有代表性的指标,进行类群间相关属性的差异性比较;
- 使用 pd.crosstab() 创建交叉表,分析医疗机构上半年和下半年的类群迁移情况;
- 根据上述分析判断医疗机构是否疑似存在医疗保险欺诈行为,并输出医疗机构信息;
- 采用轮廓系数和 Calinski-Harabasz 指标对投保人和医疗机构的聚类模型进行评价,并可视化评价结果。
七 提交要求
- 提交实现本实践任务的所有代码(可执行,非.doc、.txt等文本格式);
- 提交综合实践任务书(word格式),包括小组成员分工、分析目的、数据预处理、算法介绍、结果分析等内容;
- 提交预处理之后的数据集,以及所有可视化图表。
代码块
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt# 显示中文和负号
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
import seaborn as sns
from sklearn.cluster import KMeans
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import silhouette_score, calinski_harabasz_score
import ospolicy_holder = pd.read_csv('./data/Policy_Holder.csv', encoding='gbk')
provider = pd.read_csv('./data/Provider.csv', encoding='gbk')
claim = pd.read_csv('./data/Claim.csv', encoding='gbk')policy_holder = policy_holder[(policy_holder['Age'] >= 0) & (policy_holder['Age'] <= 100)]# 统一日期格式
claim['住院开始时间'] = pd.to_datetime(claim['住院开始时间'], errors='coerce')
claim['住院结束时间'] = pd.to_datetime(claim['住院结束时间'], errors='coerce')# Policy_HolderID,ProgramCode,MEDcode,Age,Sex 转换成 投保人编号,保险条款,治疗措施编码,年龄,性别
field_mapping1 = {"Policy_HolderID": "投保人编号","ProgramCode": "保险条款","MEDcode": "治疗措施编码","Age": "年龄","Sex": "性别"
}
# 更换表头
policy_holder.rename(columns=field_mapping1, inplace=True)
# print(policy_holder.head())
# 列名称映射字典
field_mapping2 = {"ProviderID": "医疗机构编号","ProviderType": "医疗机构大类","ProviderSpecialty": "医疗机构细类","Location": "位置编码"
}
# 更换表头
provider.rename(columns=field_mapping2, inplace=True)
# 打印处理后的数据,以确认变化
print(provider.head())# 保存处理后的数据
output_file = '结果分析'
if not os.path.exists(f'{output_file}'):os.mkdir(f'{output_file}')
provider.to_csv(f'./{output_file}/Provider.csv', index=False)
claim.to_csv(f'./{output_file}/Claim.csv', index=False)
policy_holder.to_csv(f'./{output_file}/Policy_Holder.csv', index=False)
三 数据准备
# 读取数据集
policy_holder = pd.read_csv('结果分析/Policy_Holder.csv')
provider = pd.read_csv('结果分析/Provider.csv')
claim = pd.read_csv('结果分析/Claim.csv')# # 查看数据集的前几行
# print(policy_holder.head())
# print(provider.head())
# print(claim.head())print(policy_holder.shape)
print(claim.shape)
print(provider.shape)
# 1 描述性统计分析
print(policy_holder.describe())
print(provider.describe())
print(claim.describe())
# 2 缺失值、重复值处理
# 缺失值处理
print(policy_holder.isnull().sum())
print(provider.isnull().sum())
print(claim.isnull().sum())# 重复值处理
policy_holder.drop_duplicates(inplace=True)
provider.drop_duplicates(inplace=True)
claim.drop_duplicates(inplace=True)
# 绘制饼图分析保险条款种类
insurance_counts = policy_holder['保险条款'].value_counts()# 绘制饼图
plt.figure(figsize=(8, 6))
plt.pie(insurance_counts, labels=insurance_counts.index, autopct='%1.1f%%', startangle=90)
plt.title('保险条款种类分布')
plt.axis('equal') # 保证饼图是圆的
plt.show()
# 绘制条形图分析治疗措施编码类别
plt.figure(figsize=(10, 6))
sns.countplot(data=policy_holder, x='治疗措施编码')
plt.title('治疗措施编码类别分布')
plt.xticks(rotation=45)
plt.show()
# 绘制条形图分析投保人所投保险的年龄分布情况
plt.figure(figsize=(10, 6))
sns.histplot(policy_holder['年龄'], bins=30, kde=True)
plt.title('投保人年龄分布情况')
plt.xlabel('年龄')
plt.ylabel('频率')
plt.show()
# 绘制条形图分析医疗机构大类分布
plt.figure(figsize=(10, 6))
provider_counts = provider['医疗机构大类'].value_counts() # 医疗机构大类数量
sns.barplot(x=provider_counts.index, y=provider_counts.values)
plt.title('医疗机构大类分布情况')
plt.xlabel('医疗机构大类')
plt.ylabel('数量')
plt.xticks(rotation=45)
plt.show()
4 特征工程
# 3 根据投保人的住院开始时间将其划分为上半年(1Y)和下半年(2Y)两部分
# 3 合并数据集
merged_df = pd.merge(policy_holder, claim, on='投保人编号', how='inner')
merged_df = pd.merge(merged_df, provider, on='医疗机构编号', how='inner')
print(merged_df.shape)# 将住院开始时间转换为 datetime 格式
merged_df['住院开始时间'] = pd.to_datetime(merged_df['住院开始时间'])
# 新建一个列 '时间段',将住院开始时间划分为 '1Y' 和 '2Y' 两部分
merged_df['时间段'] = merged_df['住院开始时间'].apply(lambda x: '1Y' if x.month < 7 else '2Y')
print(merged_df.head())
print(merged_df.dtypes)
print(merged_df.shape)
# 5 将投保人信息中的年龄、性别、治疗措施编码、保险条款属性特征进行编码转换,按值展开(年龄除外);查看保险条例有那些
# 查看保险条例有那些
print(merged_df['保险条款'].unique())
# 查看治疗措施编码有那些
print(merged_df['治疗措施编码'].unique())
# 性别编码
gender_mapping = {'男': 1, '女': 0}
merged_df['性别'] = merged_df['性别'].map(gender_mapping)
# 保险条款编码
insurance_mapping = {'老年保障险': 0, '伤残险': 1}
merged_df['保险条款'] = merged_df['保险条款'].map(insurance_mapping)
# 治疗措施编码
cure_mapping = {'MCD': 0, 'RegularMedicare': 1, 'MCbuy': 2, 'Undocumented': 3, 'MCQ': 4}
merged_df['治疗措施编码'] = merged_df['治疗措施编码'].map(cure_mapping)print(merged_df[['性别', '年龄', '保险条款', '治疗措施编码']].head(20))
print(merged_df[['性别', '年龄', '保险条款', '治疗措施编码']].dtypes)
print(merged_df[['性别', '年龄', '保险条款', '治疗措施编码']].shape)
[‘伤残险’ ‘老年保障险’]
[‘MCQ’ ‘RegularMedicare’ ‘MCD’ ‘MCbuy’ ‘Undocumented’]
性别 年龄 保险条款 治疗措施编码
0 0 90 1 4
1 0 90 1 4
2 0 90 1 4
3 0 90 1 4
4 0 90 1 4
5 0 90 1 4
6 0 90 1 4
7 0 90 1 4
8 0 90 1 4
9 0 90 1 4
10 0 90 1 4
11 0 90 1 4
12 0 90 1 4
13 0 90 1 4
14 0 90 1 4
15 0 90 1 4
16 0 90 1 4
17 0 90 1 4
18 0 90 1 4
19 0 90 1 4
性别 int64
年龄 int64
保险条款 int64
治疗措施编码 int64
dtype: object
(9448, 4)
医疗机构大类:
['emerg phys' 'hear aid' 'home health' 'hosp - III' 'optometrist''pharmacist' 'physician' 'podiatrist' 'prsth/ortho' 'trans amb only''psychiatrist' 'amb srg center' 'optician' 'school dist' 'dme suppliers''anesthesiologist' 'nurse svc' 'substance abuse' 'oxy - cont' 'hmo''lab fac' 'day health' 'maternity' 'phys therapist' 'oxy - noncontract''radiology' 'neuromuscular ctr' 'psychologist']
医疗机构细类:
['gen pract' 'med supply' 'non-phys' 'hosp peer 5' 'optical' 'radiology''podiatry' 'med trans' 'clinic' 'psychiatry' 'misc physician''hosp peer 3' 'pathology' 'hosp peer 4' 'fam pract' 'anesthesia''dermatology' 'gen surg' 'int medicine' '31' 'lab diagnostic''ophthalmology' 'fed qual health ctr' 'ob-gyn' 'phys therapy'
# 10 通过编码转换将医疗机构大类、医疗机构细类转化为数值型数据;
# print(merged_df.dtypes)
# print('医疗机构大类:\n',merged_df['医疗机构大类'].unique())
# print('医疗机构细类:\n',merged_df['医疗机构细类'].unique())
# 医疗机构大类映射字典
institution_type_mapping = {'amb srg center': 0, 'anesthesiologist': 1, 'day health': 2, 'dme suppliers': 3,'emerg phys': 4, 'hear aid': 5, 'hmo': 6, 'home health': 7, 'hosp - III': 8, 'lab fac': 9,'maternity': 10,'neuromuscular ctr': 11, 'nurse svc': 12, 'optician': 13, 'optometrist': 14,'oxy - cont': 15, 'oxy - noncontract': 16, 'pharmacist': 17, 'phys therapist': 18,'physician': 19,'podiatrist': 20, 'prsth/ortho': 21, 'psychiatrist': 22, 'psychologist': 23,'radiology': 24,'school dist': 25, 'substance abuse': 26, 'trans amb only': 27}# 医疗机构细类映射字典
institution_subtype_mapping = {'misc physician': 0, 'anesthesia': 1, 'clinic': 2, 'med supply': 3, 'gen pract': 4,'31': 5, 'non-phys': 6, 'hosp peer 3': 7, 'hosp peer 4': 8, 'hosp peer 5': 9,'lab diagnostic': 10, 'pathology': 11,'fed qual health ctr': 12, 'speech path': 13, 'optical': 14, 'phys therapy': 15,'cardiovasc': 16,'dermatology': 17, 'endocrinology': 18, 'ent': 19, 'fam pract': 20, 'gastroent': 21,'gen surg': 22,'int medicine': 23, 'nephrology': 24, 'neuro surg': 25, 'neurology': 26, 'ob-gyn': 27,'oncology': 28,'ophthalmology': 29, 'ortho surgery': 30, 'pediatrics': 31, 'pulmonary': 32,'radiology': 33,'podiatry': 34, 'psychiatry': 35, 'psychology': 36, 'med trans': 37}
# 应用医疗机构大类映射字典
merged_df['医疗机构大类'] = merged_df['医疗机构大类'].map(institution_type_mapping)
# 应用医疗机构细类映射字典
merged_df['医疗机构细类'] = merged_df['医疗机构细类'].map(institution_subtype_mapping)
print(merged_df.head())
print(merged_df.dtypes)
print(merged_df.shape)
投保人编号 保险条款 治疗措施编码 年龄 性别 索赔编号 医疗机构编号 \
0 11700010743 1 4 90 0 217460001631126 10017613409
1 11700010743 1 4 90 0 217460001631126 10017613409
2 11700010743 1 4 90 0 196080001529503 10019614301
3 11700010743 1 4 90 0 196080001529503 10019614301
4 11700010743 1 4 90 0 196080001529503 10019614301
投保人状态 医疗机构服务类别 诊断 ... 住院开始时间 住院结束时间 保费覆盖额 \
0 StillPatient OutpatientOther V72.4 … 2001-06-12 2001-06-19 14800
1 HomeDischarge OutpatientOther V72.2 … 2001-04-24 2001-04-29 78200
2 Missing PhysicianOther 518.1 … 2001-12-04 2001-12-05 9500
3 Missing PhysicianOther 707.2 … 2001-12-04 2001-12-05 16200
4 StillPatient PhysicianOther 707.2 … 2001-12-18 2001-12-20 74700
账单金额 支付金额 服务地点 医疗机构大类 医疗机构细类 位置编码 时间段
0 14926 14739 nh_snf 4 4 104 1Y
1 78457 78161 nh_icf 4 4 104 1Y
2 10512 9443 office 5 3 410 2Y
3 16714 15845 office 5 3 410 2Y
4 74930 74108 nh_snf 5 3 410 2Y
[5 rows x 22 columns]
投保人编号 int64
保险条款 int64
治疗措施编码 int64
年龄 int64
性别 int64
索赔编号 int64
医疗机构编号 int64
投保人状态 object
医疗机构服务类别 object
诊断 object
处理过程代码 int64
住院时长 int64
住院开始时间 datetime64[ns]
住院结束时间 object
保费覆盖额 int64
账单金额 int64
支付金额 int64
服务地点 object
医疗机构大类 int64
医疗机构细类 int64
位置编码 int64
时间段 object
dtype: object
(9448, 22)
# 1 结合投保人信息和索赔信息,挑选有效的投保人相关特征# 选择有效的特征
selected_features_1 = merged_df[['投保人编号','保费覆盖额','账单金额','支付金额','保险条款','治疗措施编码','年龄','性别','时间段',]
]policy_holder_features = merged_df[selected_features_1.columns]
print(policy_holder_features.head())
print(policy_holder_features.dtypes)
print(policy_holder_features.shape)
医疗机构编号 投保人编号 保费覆盖额 账单金额 支付金额 服务地点 医疗机构大类 医疗机构细类 位置编码
0 10017613409 11700010743 14800 14926 14739 nh_snf 4 4 104
1 10017613409 11700010743 78200 78457 78161 nh_icf 4 4 104
2 10019614301 11700010743 9500 10512 9443 office 5 3 410
3 10019614301 11700010743 16200 16714 15845 office 5 3 410
4 10019614301 11700010743 74700 74930 74108 nh_snf 5 3 410
医疗机构编号 int64
投保人编号 int64
保费覆盖额 int64
账单金额 int64
支付金额 int64
服务地点 object
医疗机构大类 int64
医疗机构细类 int64
位置编码 int64
dtype: object
# 4 选取索赔订单中保费覆盖额、账单金额、支付金额特征,分别按上半年和下半年进行统计,从而得到投保人半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数;
# 选取索赔订单中保费覆盖额、账单金额、支付金额特征
selected_features_3 = merged_df[['投保人编号','保费覆盖额','账单金额','支付金额','时间段',]
]
# 按时间段统计特征
grouped_features = selected_features_3.groupby(['投保人编号', '时间段']).agg(保费覆盖额=('保费覆盖额', 'sum'),账单金额=('账单金额', 'sum'),支付金额=('支付金额', 'sum'),支付金额笔数=('支付金额', 'count')
).reset_index()
print('投保人半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数:')
print(grouped_features.head())
print(grouped_features.dtypes)
print(grouped_features.shape)
保人半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数:
投保人编号 时间段 保费覆盖额 账单金额 支付金额 支付金额笔数
0 10300010118 1Y 1240200 1254890 1225159 30
1 10300010118 2Y 1051400 1065577 1039617 24
2 10500010200 1Y 247800 250520 245811 4
3 10600010291 2Y 785400 797520 776217 21
4 10700010301 2Y 43800 44633 43172 1
投保人编号 int64
时间段 object
保费覆盖额 int64
账单金额 int64
支付金额 int64
支付金额笔数 int64
dtype: object
(433, 6)
# 6.组合 4 和 5 中的特征,构成投保人特征;
# 从原始数据中选择需要合并的属性
additional_features = merged_df[['投保人编号','年龄','性别','治疗措施编码','保险条款','时间段',]
].drop_duplicates(subset='投保人编号')
# 合并数据
final_features = grouped_features.merge(additional_features, on=['投保人编号', '时间段'], how='inner')
# print('投保人特征:')
# print(final_features.head())
# print(final_features.dtypes)
# print(final_features.shape)
# 清理重复值
selected_features_4 = merged_df[['投保人编号','性别','年龄','保险条款','治疗措施编码','时间段',]
].drop_duplicates(inplace=False)# 合并特征
policyholder_characteristics = pd.merge(grouped_features, selected_features_4, on=['投保人编号', '时间段'], how='inner')print('投保人特征:')
print(policyholder_characteristics.head())
print(policyholder_characteristics.dtypes)
print(policyholder_characteristics.shape)
投保人特征:
投保人编号 时间段 保费覆盖额 账单金额 支付金额 支付金额笔数 性别 年龄 保险条款 治疗措施编码
0 10300010118 1Y 1240200 1254890 1225159 30 1 27 1 1
1 10300010118 2Y 1051400 1065577 1039617 24 1 27 1 1
2 10500010200 1Y 247800 250520 245811 4 1 49 1 1
3 10600010291 2Y 785400 797520 776217 21 1 28 1 1
4 10700010301 2Y 43800 44633 43172 1 0 92 1 1
投保人编号 int64
时间段 object
保费覆盖额 int64
账单金额 int64
支付金额 int64
支付金额笔数 int64
性别 int64
年龄 int64
保险条款 int64
治疗措施编码 int64
dtype: object
(433, 10)
# 7.按医疗机构编号和所属时间段分组,统计投保人数和处理过程数量;
number_of_providers_grouped = merged_df.groupby(['医疗机构编号', '时间段']).agg(投保总人数=('投保人编号', 'count'),处理过程数量=('处理过程代码', 'count')
).reset_index()
print('按医疗机构编号和所属时间段分组,统计投保人数和处理过程数量:')
print(number_of_providers_grouped.head(20))
print(number_of_providers_grouped.dtypes)
print(number_of_providers_grouped.shape)
按医疗机构编号和所属时间段分组,统计投保人数和处理过程数量:
医疗机构编号 时间段 投保总人数 处理过程数量
0 10010410197 1Y 2 2
1 10010610221 1Y 2 2
2 10010710275 1Y 5 5
3 10010710275 2Y 6 6
4 10010910429 1Y 46 46
5 10010910429 2Y 33 33
6 10011210467 2Y 1 1
7 10011310519 2Y 1 1
8 10011410627 2Y 7 7
9 10011610682 1Y 1 1
10 10011810781 1Y 1 1
11 10011910901 1Y 1 1
12 10012110993 1Y 4 4
13 10012110993 2Y 6 6
14 10012611269 1Y 17 17
15 10012611269 2Y 5 5
16 10012811329 1Y 10 10
17 10012811329 2Y 8 8
18 10013011335 1Y 12 12
19 10013011335 2Y 4 4
医疗机构编号 int64
时间段 object
投保总人数 int64
处理过程数量 int64
dtype: object
(717, 4)
# 8. 选取索赔订单中保费覆盖额、账单金额、支付金额特征,分别按上半年和下半年进行统计,从而得到医疗机构半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数;
selected_features_5 = merged_df[['医疗机构编号','投保人编号','保费覆盖额','账单金额','支付金额','时间段']
]
provider_grouped_features = selected_features_5.groupby(['医疗机构编号', '时间段']).agg(投保人数=('投保人编号', 'count'),保费覆盖额=('保费覆盖额', 'sum'),账单金额=('账单金额', 'sum'),支付金额=('支付金额', 'sum'),支付金额笔数=('支付金额', 'count')
).reset_index()
print('医疗机构投保人数、半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数:')
print(provider_grouped_features.head(30))
print(provider_grouped_features.dtypes)
print(provider_grouped_features.shape)
医疗机构投保人数、半年保费覆盖额、半年账单金额、半年支付金额和半年支付金额笔数:
医疗机构编号 时间段 投保人数 保费覆盖额 账单金额 支付金额 支付金额笔数
0 10010410197 1Y 2 64500 66133 63381 2
1 10010610221 1Y 2 41300 42029 40428 2
2 10010710275 1Y 5 201900 206048 199630 5
3 10010710275 2Y 6 252300 256399 249037 6
4 10010910429 1Y 46 1705500 1728328 1681188 46
5 10010910429 2Y 33 1533200 1549347 1513867 33
6 10011210467 2Y 1 83400 83877 82791 1
7 10011310519 2Y 1 99900 99925 99674 1
8 10011410627 2Y 7 245500 250332 240543 7
9 10011610682 1Y 1 32400 32639 31442 1
10 10011810781 1Y 1 45100 45618 44556 1
11 10011910901 1Y 1 21200 22073 20871 1
12 10012110993 1Y 4 161600 163706 159618 4
13 10012110993 2Y 6 252200 255580 249057 6
14 10012611269 1Y 17 504500 512906 495515 17
15 10012611269 2Y 5 199000 201279 196669 5
16 10012811329 1Y 10 359700 364645 353434 10
17 10012811329 2Y 8 230600 235969 226320 8
18 10013011335 1Y 12 656700 660945 649501 12
19 10013011335 2Y 4 262500 264325 260483 4
20 10013111430 1Y 4 181800 184076 178648 4
21 10013111430 2Y 4 254400 256127 253272 4
22 10013311562 1Y 3 59600 60529 57231 3
23 10013311562 2Y 6 237100 240810 233658 6
24 10013511628 1Y 4 148700 150800 146582 4
25 10013511628 2Y 1 12000 12966 11112 1
26 10013611711 1Y 5 166700 169037 164376 5
27 10013611711 2Y 5 169700 171198 167818 5
28 10013811720 1Y 5 157300 159500 154269 5
29 10013811720 2Y 7 296500 300456 293180 7
医疗机构编号 int64
时间段 object
投保人数 int64
保费覆盖额 int64
账单金额 int64
支付金额 int64
支付金额笔数 int64
dtype: object
(717, 7)
# 9.选取医疗机构信息中的医疗机构大类、医疗机构细类和位置编码,与 7和 8 中的特征,构成医疗机构特征;
additional_features_2 = merged_df[['医疗机构编号','医疗机构大类','医疗机构细类','位置编码','时间段']
].drop_duplicates(subset='医疗机构编号')
# 合并数据
final_features_2 = provider_grouped_features.merge(additional_features_2, on=['医疗机构编号', '时间段'], how='inner')
# print('医疗机构特征:')
# print(final_features_2.head())
# print(final_features_2.dtypes)
# print(final_features_2.shape)
# 清理重复值
selected_features_6 = merged_df[['医疗机构编号','医疗机构大类','医疗机构细类','位置编码','时间段']
].drop_duplicates(inplace=False)
# 合并特征
provider_characteristics = pd.merge(provider_grouped_features, selected_features_6, on=['医疗机构编号', '时间段'],how='inner')
print('医疗机构特征:')
print(provider_characteristics.head(20))
print(provider_characteristics.dtypes)
print(provider_characteristics.shape)
provider_characteristics.to_csv('结果分析/provider_characteristics.csv', index=False, encoding='utf-8')
医疗机构特征:
医疗机构编号 时间段 投保人数 保费覆盖额 账单金额 支付金额 支付金额笔数 医疗机构大类 医疗机构细类
0 10010410197 1Y 2 64500 66133 63381 2 0 0
1 10010610221 1Y 2 41300 42029 40428 2 0 0
2 10010710275 1Y 5 201900 206048 199630 5 1 1
3 10010710275 2Y 6 252300 256399 249037 6 1 1
4 10010910429 1Y 46 1705500 1728328 1681188 46 1 1
5 10010910429 2Y 33 1533200 1549347 1513867 33 1 1
6 10011210467 2Y 1 83400 83877 82791 1 1 1
7 10011310519 2Y 1 99900 99925 99674 1 1 1
8 10011410627 2Y 7 245500 250332 240543 7 1 1
9 10011610682 1Y 1 32400 32639 31442 1 1 1
10 10011810781 1Y 1 45100 45618 44556 1 1 1
11 10011910901 1Y 1 21200 22073 20871 1 1 1
12 10012110993 1Y 4 161600 163706 159618 4 1 1
13 10012110993 2Y 6 252200 255580 249057 6 1 1
14 10012611269 1Y 17 504500 512906 495515 17 2 2
15 10012611269 2Y 5 199000 201279 196669 5 2 2
16 10012811329 1Y 10 359700 364645 353434 10 2 2
17 10012811329 2Y 8 230600 235969 226320 8 2 2
18 10013011335 1Y 12 656700 660945 649501 12 2 2
19 10013011335 2Y 4 262500 264325 260483 4 2 2
位置编码
0 410
1 104
2 104
3 104
4 410
5 410
6 367
7 410
8 104
9 410
10 367
11 367
12 410
13 410
14 410
15 410
16 367
17 367
18 410
19 410
医疗机构编号 int64
时间段 object
投保人数 int64
保费覆盖额 int64
账单金额 int64
支付金额 int64
支付金额笔数 int64
医疗机构大类 int64
医疗机构细类 int64
位置编码 int64
dtype: object
(717, 10)
六、模型训练和性能度量
# 1.将投保人聚类成 5 类类群,选取投保人数量、平均支付金额、平均支付笔数等具有代表性的指标,进行类群间的差异性比较;# 选取用于聚类的特征
cluster_features = policyholder_characteristics[['投保人编号', '保费覆盖额', '账单金额', '支付金额', '支付金额笔数', '年龄']
]# 聚类
kmeans = KMeans(n_clusters=5, random_state=42)
cluster_labels = kmeans.fit_predict(cluster_features)# 将聚类标签添加到原始特征中
cluster_features['聚类标签'] = cluster_labels# 计算各聚类的代表性指标
cluster_summary = cluster_features.groupby('聚类标签').agg(投保人数量=('投保人编号', 'count'),平均支付金额=('支付金额', 'mean'),平均支付笔数=('支付金额笔数', 'mean')
).reset_index()print('聚类结果:')
print(cluster_summary)# 详细描述每个聚类类群
for label in range(5):cluster_info = cluster_features[cluster_features['聚类标签'] == label]print(f'聚类 {label} 详细信息:')print(f'投保人数量:{len(cluster_info)}')print(f'平均支付金额:{cluster_info["支付金额"].mean()}')print(f'平均支付笔数:{cluster_info["支付金额笔数"].mean()}')print('\n')
聚类结果:
聚类标签 投保人数量 平均支付金额 平均支付笔数
0 0 91 7.490569e+05 19.406593
1 1 92 1.002300e+06 26.434783
2 2 78 9.326763e+05 24.487179
3 3 77 6.980571e+05 18.532468
4 4 95 7.660895e+05 20.136842
聚类 0 详细信息:
投保人数量:91
平均支付金额:749056.8791208791
平均支付笔数:19.406593406593405
聚类 1 详细信息:
投保人数量:92
平均支付金额:1002299.5869565217
平均支付笔数:26.434782608695652
聚类 2 详细信息:
投保人数量:78
平均支付金额:932676.3461538461
平均支付笔数:24.487179487179485
聚类 3 详细信息:
投保人数量:77
平均支付金额:698057.077922078
平均支付笔数:18.532467532467532
聚类 4 详细信息:
投保人数量:95
平均支付金额:766089.5368421052
平均支付笔数:20.13684210526316
# 2.分析投保人上半年和下半年的索赔变化情况,即投保人类群的迁移统计
# 选取上半年和下半年的数据
first_half = policyholder_characteristics[policyholder_characteristics['时间段'] == '1Y']
second_half = policyholder_characteristics[policyholder_characteristics['时间段'] == '2Y']
# print(first_half)
# print(first_half.dtypes)
# print(first_half.shape)# (232, 10)
# print(second_half)
# print(second_half.dtypes)
# print(second_half.shape)# (201, 10)
# 分别计算上半年和下半年的统计数据
first_half_summary = first_half.groupby('投保人编号').agg(上半年支付金额=('支付金额', 'sum'),上半年支付笔数=('支付金额笔数', 'sum')
).reset_index()
second_half_summary = second_half.groupby('投保人编号').agg(下半年支付金额=('支付金额', 'sum'),下半年支付笔数=('支付金额笔数', 'sum')
).reset_index()
# 合并上半年和下半年的数据
summary = pd.merge(first_half_summary, second_half_summary, on='投保人编号', how='outer').fillna(0)
print('上半年和下半年的支付金额和支付笔数:')
# print(summary)
# print(summary.dtypes)
# print(summary.shape) # (304, 5)
# 计算变化情况
summary['支付金额变化'] = summary['下半年支付金额'] - summary['上半年支付金额']
summary['支付笔数变化'] = summary['下半年支付笔数'] - summary['上半年支付笔数']print('索赔变化情况:', summary)
# 按照支付金额和支付笔数的变化情况进行排序
summary = summary.sort_values(by=['支付金额变化', '支付笔数变化'], ascending=True)
print('按照支付金额和支付笔数的变化情况进行排序:', summary)
summary.to_csv('结果分析/summary.csv', index=False, encoding='utf-8')
上半年和下半年的支付金额和支付笔数:
索赔变化情况: 投保人编号 上半年支付金额 上半年支付笔数 下半年支付金额 下半年支付笔数 支付金额变化 支付笔数变化
0 10300010118 1225159.0 30.0 1039617.0 24.0 -185542.0 -6.0
1 10500010200 245811.0 4.0 0.0 0.0 -245811.0 -4.0
2 10600010291 0.0 0.0 776217.0 21.0 776217.0 21.0
3 10700010301 0.0 0.0 43172.0 1.0 43172.0 1.0
4 10800010360 814066.0 20.0 0.0 0.0 -814066.0 -20.0
… … … … … … … …
299 68400038040 664169.0 14.0 53343.0 2.0 -610826.0 -12.0
300 68500038167 3550826.0 88.0 1227891.0 40.0 -2322935.0 -48.0
301 68900038289 2144262.0 51.0 1457407.0 39.0 -686855.0 -12.0
302 69100038371 279869.0 8.0 450453.0 10.0 170584.0 2.0
303 69300038437 667440.0 19.0 335496.0 11.0 -331944.0 -8.0
[304 rows x 7 columns]
按照支付金额和支付笔数的变化情况进行排序: 投保人编号 上半年支付金额 上半年支付笔数 下半年支付金额 下半年支付笔数 支付金额变化 支付笔数变化
300 68500038167 3550826.0 88.0 1227891.0 40.0 -2322935.0 -48.0
222 53700030960 2142648.0 44.0 502231.0 16.0 -1640417.0 -28.0
184 46300027224 2696993.0 66.0 1260491.0 33.0 -1436502.0 -33.0
153 40400023860 2092201.0 54.0 673028.0 27.0 -1419173.0 -27.0
31 16500013059 2494316.0 61.0 1111212.0 32.0 -1383104.0 -29.0
… … … … … … … …
138 37500022787 0.0 0.0 864267.0 19.0 864267.0 19.0
290 66800037290 0.0 0.0 986847.0 24.0 986847.0 24.0
285 65500036707 236604.0 6.0 4259995.0 117.0 4023391.0 111.0
291 67000037367 279731.0 8.0 4721788.0 116.0 4442057.0 108.0
107 31200019485 198910.0 5.0 5420315.0 139.0 5221405.0 134.0
[304 rows x 7 columns]
# 3.根据 1 和 2 判断投保人是否疑似存在医疗保险欺诈行为,并输出投保人信息;
# 使用 Isolation Forest 算法确定欺诈标准
from sklearn.ensemble import IsolationForestfeatures = summary[['支付金额变化', '支付笔数变化']]
isolation_forest = IsolationForest(contamination=0.1, random_state=42)
isolation_forest.fit(features)# 预测异常值
anomaly_scores = isolation_forest.decision_function(features)
outliers = isolation_forest.predict(features)# 找出疑似欺诈投保人
fraud_suspects = summary[outliers == -1]
# 输出疑似欺诈投保人的信息
print('疑似欺诈投保人信息:')
print(fraud_suspects)
# print(fraud_suspects.dtypes)
# print(fraud_suspects.shape)
# 获取疑似欺诈投保人的详细信息
suspicious_policyholder = policyholder_characteristics[policyholder_characteristics['投保人编号'].isin(fraud_suspects['投保人编号'])]# 输出详细信息
print('疑似欺诈投保人的详细信息:')
print(suspicious_policyholder)
suspicious_policyholder.to_csv('结果分析/suspicious_policyholder.csv', index=False, encoding='utf-8')# 在 policyholder_characteristics 中增加一列 "是否疑似欺诈"
policyholder_characteristics['是否疑似欺诈'] = 0# 更新疑似欺诈投保人的标记
fraud_indices = fraud_suspects['投保人编号']
policyholder_characteristics.loc[policyholder_characteristics['投保人编号'].isin(fraud_indices), '是否疑似欺诈'] = 1# 输出最终结果
print('投保人的详细信息(包含是否疑似欺诈标记):')
print(policyholder_characteristics)# 保存结果
policyholder_characteristics.to_csv('结果分析/policyholder_characteristics.csv', index=False, encoding='utf-8')
疑似欺诈投保人信息:
投保人编号 上半年支付金额 上半年支付笔数 下半年支付金额 下半年支付笔数 支付金额变化 支付笔数变化
300 68500038167 3550826.0 88.0 1227891.0 40.0 -2322935.0 -48.0
222 53700030960 2142648.0 44.0 502231.0 16.0 -1640417.0 -28.0
184 46300027224 2696993.0 66.0 1260491.0 33.0 -1436502.0 -33.0
153 40400023860 2092201.0 54.0 673028.0 27.0 -1419173.0 -27.0
31 16500013059 2494316.0 61.0 1111212.0 32.0 -1383104.0 -29.0
225 54500031427 3210912.0 75.0 1892716.0 58.0 -1318196.0 -17.0
164 42400024932 2706637.0 64.0 1408757.0 39.0 -1297880.0 -25.0
16 12900011570 3000471.0 77.0 1773806.0 49.0 -1226665.0 -28.0
209 51400029676 1215350.0 30.0 0.0 0.0 -1215350.0 -30.0
244 58200033387 2048805.0 54.0 849658.0 20.0 -1199147.0 -34.0
199 49600028891 1190440.0 32.0 0.0 0.0 -1190440.0 -32.0
105 31000019438 3026729.0 68.0 1863217.0 51.0 -1163512.0 -17.0
147 39300023401 1334020.0 40.0 189494.0 5.0 -1144526.0 -35.0
252 60000033912 1830418.0 53.0 777558.0 20.0 -1052860.0 -33.0
59 21700015465 2802028.0 80.0 1926306.0 46.0 -875722.0 -34.0
44 19000014307 2522924.0 78.0 1733163.0 40.0 -789761.0 -38.0
279 64500036177 1889143.0 56.0 1209423.0 28.0 -679720.0 -28.0
229 55200031755 2094073.0 55.0 2291033.0 71.0 196960.0 16.0
217 52600030461 0.0 0.0 581719.0 15.0 581719.0 15.0
6 11200010435 0.0 0.0 624589.0 17.0 624589.0 17.0
241 57600033140 0.0 0.0 638502.0 14.0 638502.0 14.0
90 28000018414 0.0 0.0 643894.0 17.0 643894.0 17.0
32 16700013144 13585.0 1.0 723847.0 21.0 710262.0 20.0
150 39800023603 683897.0 20.0 1430553.0 33.0 746656.0 13.0
2 10600010291 0.0 0.0 776217.0 21.0 776217.0 21.0
73 24800016667 0.0 0.0 797242.0 17.0 797242.0 17.0
138 37500022787 0.0 0.0 864267.0 19.0 864267.0 19.0
290 66800037290 0.0 0.0 986847.0 24.0 986847.0 24.0
285 65500036707 236604.0 6.0 4259995.0 117.0 4023391.0 111.0
291 67000037367 279731.0 8.0 4721788.0 116.0 4442057.0 108.0
107 31200019485 198910.0 5.0 5420315.0 139.0 5221405.0 134.0
# 4.将医疗机构聚类成 5 类类群,选取医疗机构数量、平均投保人数量、平均账单金额等具有代表性的指标,进行类群间相关属性的差异性比较
# 选取用于聚类的特征
cluster_features_2 = provider_characteristics[['医疗机构编号', '保费覆盖额', '账单金额', '支付金额', '支付金额笔数']
]
# 聚类
kmeans = KMeans(n_clusters=5, random_state=42)
cluster_labels = kmeans.fit_predict(cluster_features_2)# 将聚类标签添加到原始特征中
provider_characteristics['聚类标签'] = cluster_labels# 计算各聚类的代表性指标
cluster_summary = provider_characteristics.groupby('聚类标签').agg(医疗机构数量=('医疗机构编号', 'count'),平均投保人数量=('投保人数', 'mean'),平均账单金额=('账单金额', 'mean'),
).reset_index()print('聚类结果:')
print(cluster_summary)# 详细描述每个聚类类群
for label in range(5):cluster_info = provider_characteristics[provider_characteristics['聚类标签'] == label]print(f'聚类 {label} 详细信息:')print(f'医疗机构数量:{len(cluster_info)}')print(f'平均投保人数量:{cluster_info["投保人数"].mean()}')print(f'平均账单金额:{cluster_info["账单金额"].mean()}')
print(provider_characteristics)
聚类结果:
聚类标签 医疗机构数量 平均投保人数量 平均账单金额
0 0 133 11.428571 448874.586466
1 1 151 20.099338 798955.602649
2 2 164 18.054878 696830.981707
3 3 130 7.076923 271182.838462
4 4 139 7.280576 282314.093525
聚类 0 详细信息:
医疗机构数量:133
平均投保人数量:11.428571428571429
平均账单金额:448874.58646616543
聚类 1 详细信息:
医疗机构数量:151
平均投保人数量:20.09933774834437
平均账单金额:798955.6026490066
聚类 2 详细信息:
医疗机构数量:164
平均投保人数量:18.054878048780488
平均账单金额:696830.981707317
聚类 3 详细信息:
医疗机构数量:130
平均投保人数量:7.076923076923077
平均账单金额:271182.8384615385
聚类 4 详细信息:
医疗机构数量:139
平均投保人数量:7.280575539568345
平均账单金额:282314.09352517984
医疗机构编号 时间段 投保人数 保费覆盖额 账单金额 支付金额 支付金额笔数 医疗机构大类 医疗机构细类
0 10010410197 1Y 2 64500 66133 63381 2 0 0
1 10010610221 1Y 2 41300 42029 40428 2 0 0
2 10010710275 1Y 5 201900 206048 199630 5 1 1
3 10010710275 2Y 6 252300 256399 249037 6 1 1
4 10010910429 1Y 46 1705500 1728328 1681188 46 1 1
… … … … … … … … … …
712 10085944077 2Y 35 1441700 1455746 1423935 35 27 37
713 10086044118 1Y 40 1688500 1708070 1668403 40 27 37
714 10086044118 2Y 31 1203500 1218676 1188260 31 27 37
715 10086244135 1Y 56 2085300 2116268 2058121 56 27 37
716 10086244135 2Y 93 3691800 3738201 3650195 93 27 37
位置编码 聚类标签
0 410 2
1 104 2
2 104 2
3 104 2
4 410 2
… … …
712 367 1
713 475 1
714 475 1
715 367 1
716 367 1
[717 rows x 11 columns]
# 5.使用 pd.crosstab()创建交叉表,分析医疗机构上半年和下半年的类群迁移情况
crosstab = pd.crosstab(index=provider_characteristics['聚类标签'],columns=provider_characteristics['时间段'],margins=True,margins_name='总计'
)
print('类群迁移交叉表:')
print(crosstab)
类群迁移交叉表:
时间段 1Y 2Y 总计
聚类标签
0 68 65 133
1 74 77 151
2 84 80 164
3 67 63 130
4 70 69 139
总计 363 354 717
# 6. 根据 4 和 5 判断医疗机构是否疑似存在医疗保险欺诈行为,并输出医疗机构信息
# 使用 Isolation Forest 算法确定欺诈标准# 选取用于聚类的特征
features = ['投保人数', '账单金额']# 使用 Isolation Forest 进行异常检测
isolation_forest = IsolationForest(contamination=0.1, random_state=42)
anomaly_scores = isolation_forest.fit_predict(provider_characteristics[features])# 将异常得分添加到原始特征中
provider_characteristics['异常得分'] = anomaly_scores# 筛选出疑似存在医疗保险欺诈行为的医疗机构
suspicious_hospitals = provider_characteristics[provider_characteristics['异常得分'] == -1]
print(suspicious_hospitals.shape)
print(suspicious_hospitals)# 输出疑似存在医疗保险欺诈行为的医疗机构信息
if not suspicious_hospitals.empty:print('疑似存在医疗保险欺诈行为的医疗机构信息:')print(suspicious_hospitals[['医疗机构编号', '时间段', '投保人数', '账单金额', '异常得分']])
else:print('没有发现疑似存在医疗保险欺诈行为的医疗机构。')# 保存信息
# 在 provider_characteristics 中增加一列 "是否疑似欺诈"
provider_characteristics['是否疑似欺诈'] = 0
# 更新疑似欺诈医疗机构的标记
fraud_indices = suspicious_hospitals['医疗机构编号']
provider_characteristics.loc[provider_characteristics['医疗机构编号'].isin(fraud_indices), '是否疑似欺诈'] = 1print('医疗机构的详细信息(包含是否疑似欺诈标记):')
# 删除异常得分列
provider_characteristics = provider_characteristics.drop(columns=['异常得分'])
print(provider_characteristics)provider_characteristics.to_csv('结果分析/provider_characteristics.csv', index=False, encoding='utf-8')
suspicious_hospitals.to_csv('结果分析/suspicious_hospitals.csv', index=False, encoding='utf-8')
位置编码 聚类标签 是否疑似欺诈
0 410 2 0
1 104 2 0
2 104 2 0
3 104 2 0
4 410 2 1
… … … …
712 367 1 1
713 475 1 1
714 475 1 1
715 367 1 1
716 367 1 1
[717 rows x 12 columns]
# 7. 采用轮廓系数(Silhouette Coefficient)和 Calinski-Harabasz 指标对投保人和医疗机构的聚类模型进行评价,并可视化评价结果。
# 选择投保人的特征
policy_holder_features = policyholder_characteristics[['保费覆盖额', '账单金额', '支付金额', '支付金额笔数', '年龄']]
# 选择医疗机构的特征
provider_features = provider_characteristics[['保费覆盖额', '账单金额', '支付金额', '投保人数']]# 定义轮廓系数和 Calinski-Harabasz 指标函数
def evaluate_clustering(X, n_clusters):kmeans = KMeans(n_clusters=n_clusters, random_state=42)kmeans.fit(X)labels = kmeans.labels_silhouette_coefficient = silhouette_score(X, labels)calinski_harabasz = calinski_harabasz_score(X, labels)return silhouette_coefficient, calinski_harabasz# 计算不同聚类数量下的评估指标
n_clusters_range = range(2, 11)
silhouette_policy_holder = []
silhouette_provider = []
calinski_harabasz_policy_holder = []
calinski_harabasz_provider = []for n_clusters in n_clusters_range:# 轮廓系数silhouette_coefficient, _ = evaluate_clustering(policy_holder_features, n_clusters)silhouette_policy_holder.append(silhouette_coefficient)silhouette_coefficient, _ = evaluate_clustering(provider_features, n_clusters)silhouette_provider.append(silhouette_coefficient)# Calinski-Harabasz 指标_, calinski_harabasz = evaluate_clustering(policy_holder_features, n_clusters)calinski_harabasz_policy_holder.append(calinski_harabasz)_, calinski_harabasz = evaluate_clustering(provider_features, n_clusters)calinski_harabasz_provider.append(calinski_harabasz)print('投保人的轮廓系数:', silhouette_policy_holder)
print('医疗机构的轮廓系数:', silhouette_provider)
print('投保人的Calinski-Harabasz 指标:', calinski_harabasz_policy_holder)
print('医疗机构的Calinski-Harabasz 指标:', calinski_harabasz_provider)
投保人的轮廓系数: [0.7226576777826208, 0.6498671347052347, 0.618970915450915, 0.6237002142602882, 0.5955120448436585, 0.5855233844947134, 0.5794357656635699, 0.555326045559005, 0.547333033009528]
医疗机构的轮廓系数: [0.7785465109052467, 0.7609298198820077, 0.6900841337647167, 0.6837587113024827, 0.6825109306135511, 0.6661725708984757, 0.5922838124312444, 0.5710308788377823, 0.5708757328209078]
投保人的Calinski-Harabasz 指标: [1307.7392539661294, 1244.3024083699274, 1481.7483768675309, 1932.443305130993, 2403.9120164318338, 2753.709599448324, 2603.6129576594244, 2741.3658307581945, 3218.467874193788]
医疗机构的Calinski-Harabasz 指标: [1029.6332630705906, 2016.279549942104, 2234.879350227129, 2690.536124127273, 2424.4476994513816, 3384.947478415253, 3908.10583144836, 4630.653312728189, 5288.283282198831]
# 可视化轮廓系数
plt.figure(figsize=(12, 6))plt.subplot(1, 2, 1)
plt.plot(n_clusters_range, silhouette_policy_holder, label='投保人')
plt.plot(n_clusters_range, silhouette_provider, label='医疗机构')
plt.xlabel('Number of clusters')
plt.ylabel('Silhouette Coefficient')
plt.legend()
plt.title('Silhouette Coefficient for Clustering Evaluation')# 可视化 Calinski-Harabasz 指标
plt.subplot(1, 2, 2)
plt.plot(n_clusters_range, calinski_harabasz_policy_holder, label='投保人')
plt.plot(n_clusters_range, calinski_harabasz_provider, label='医疗机构')
plt.xlabel('Number of clusters')
plt.ylabel('Calinski-Harabasz Index')
plt.legend()
plt.title('Calinski-Harabasz Index for Clustering Evaluation')plt.tight_layout()
plt.show()
轮廓系数的取值范围为[-1, 1],轮廓系数越大聚类效果越好
Calinski-Harabasz数的分数越大说明越好