汇总表 这些 0
值在 SkinThickness 和 Insulin 等属性中出现很多次,并且可能极大地影响其数据分布
检查重复行 我们的数据集中没有重复的行
属性的直方图分布 上图旨在检查每个属性分布的偏度,以确定用什么统计量来填充这些
0
值。对于对称分布(例如,SkinThickness & Glucose),我们稍后会用平均值替换这些缺失值 NaN
,然后再用平均值填充这些空值;而对于偏斜分布(例如,Insulin, BloodPressure 和 BMI),我们使用中位数来最小化误差。
左偏(负偏): 如果低端(左侧)的尾部比右侧长或更长,则该分布被视为左偏分布。在这种情况下,大多数数据值会向高端(右侧)聚集,而少数低异常值会使均值向下倾斜。因此,平均值通常小于中位数。
右偏(正偏): 右偏分布的尾部(右侧)较长或拉伸。这表明大多数数据点都集中在低端(左侧),而一些高离群值会将平均值向上拉高。在这种情况下,平均值通常大于中位数。
正态分布:关于均值对称的概率分布
还可以用以下图表示:
怀孕高峰期约为 1-2 次。平均怀孕次数约为 3.85 次,标准差为 3.37 次。这一特征表现为正偏度,偏度为 0.90。 葡萄糖水平大多在 100 至 125 毫克/分升之间,平均值约为 120 毫克/分升,标准差为 32。分布接近对称,偏度为 0.17。 舒张压的中心倾向接近 60-80mmHg,平均值为 69mmHg,分布(标准差)约为 。它显示出负偏度,其偏度值为 -1.84 皮肤厚度平均值约为 20.54 毫米,标准偏差为 。数据接近对称,偏度值为 0.11 胰岛素水平主要集中在 0 左右,但平均值上升到 79.80 左右,表明数据呈正偏度分布。偏度值为 2.27,标准偏差为 115.24,相对较高。 体重指数值集中在 30-40 附近,平均体重指数为 32,标准差为 7.88。根据-0.43的偏度值,分布呈现轻微的负偏斜 糖尿病谱系系数 在 0.3-0.4 附近最为常见,平均值为 0.47,标准差为 0.33。 年龄在 20-30 岁左右达到峰值,平均年龄约为 33.27 岁,标准偏差为 11.76。数据分布呈右偏态,偏度值为 1.13。 columns_with_zeros = [ 'Glucose' , 'BMI' , 'Insulin' , 'SkinThickness' , 'BloodPressure' ] #将不可能的零值替换为它们的空值 for column in columns_with_zeros: df[column] = df[column].replace( 0 , np.nan) binomial_columns_with_zeros = [ 'Glucose' , 'SkinThickness' ] #用空值替换不可能为零的值 - 平均值(近似对称) for column in binomial_columns_with_zeros: df[column] = df[column].fillna(df[column].mean()) skewed_columns_with_zeros = [
'BloodPressure' , 'Insulin' , 'BMI' ] #用空值替换不可能为零的值 - 中位数(偏态) for column in skewed_columns_with_zeros: df[column] = df[column].fillna(df[column].median()) df
探索性数据分析 探索性数据分析 (EDA) 就像拼图游戏一样——数据科学家需要运用他们的分析直觉。在这个阶段,我们开始使用可视化、汇总和统计工具来解答数据背后的 what , how , where 以及 why 。在这个阶段,我分析了数据集的结构、分布和关系,以发现有意义的模式并识别糖尿病的潜在预测因子。
怀孕: 怀孕次数的平均值约为 4 次,中位数为 3 次,模式为 1 次,数值范围为 0 至 17 次,标准差为 3.37 次。 葡萄糖: 葡萄糖水平平均约为 120.89 mg/dl,中位数为 117.00 mg/dl,模式为 99.00 mg/dl。数值范围从 0 到 199 mg/dl,标准差为 31.97 mg/dl。 血压:平均血压约为 69.105 毫米汞柱,中位数为 72.00 毫米汞柱,模式为 70.00 毫米汞柱。测量范围为 0 至 122 mmHg,标准差为 19.356 mmHg。 皮肤厚度: 皮肤厚度的平均值为 20.536 毫米,中位数为 23.00 毫米,模式为 0.00 毫米。范围为 0 至 99 毫米,标准差为 15.95 毫米。 胰岛素:胰岛素水平平均约为 79.80 单位,中位数为 30.50 单位,模式为 0.00 单位。范围为 0 至 846 单位,标准差为 115.24 单位。 体重指数: 平均体重指数(BMI)为 31.993,中位数和模式均为 32.00。数值范围从 0 到 67.10,标准差为 7.884。 糖尿病谱系函数: 糖尿病血统函数的平均值为 0.472,中位数为 0.372,模式为 0.254。数值介于 0.08 和 2.42 之间,标准差为 0.331。
年龄:平均年龄为 33.24 岁,中位数为 29.00 岁,模式为 22.00 岁。年龄范围在 21 至 81 岁之间,标准差为 11.76 岁。 怀孕次数 中位数约为 3-4,有几个较高的离群值(如 15+ 怀孕)。 0 这样的值不是离群值,这是合乎逻辑的。 葡萄糖 中位数约为 110-120。 由于数值较高(高达约 200),出现了明显的右斜。 包括 0 在内的极低值为异常值--在医学上难以置信,可能是数据缺失或错误。 血压 中位数约为 70-75 mmHg。 有几个值为 0,明显是异常值,不符合生理学原理。 血压分布比较对称,但这些 0 值需要清理。 皮肤厚度 中值约为 23-25 mm。 0 的数量非常多--强烈表明数据缺失或有误。 也有一些上限离群值(约 100),但主要问题是 0。 胰岛素 极度右偏。 数值差异巨大,有许多离群值(高达 800+)。 0 很常见,可能表示数据缺失--在现实世界的数据集中,胰岛素经常缺失。 体重指数 中位数约为 32。 0 值也是异常值--不太可能对体重指数有效。 其他方面呈右偏分布,但分布相当紧凑。 糖尿病谱系系数 中位数约为 0.4。 右偏,有许多轻度和极端异常值(>1.5)。 没有明显的无效值,但该属性的差异很大。 年龄 中位数约为 29-30。 右偏,尾部延伸至 80 岁。 没有明显的无效异常值;分布看起来很自然。 怀孕 糖尿病患者的怀孕次数往往较多。 结果 = 1 的怀孕次数中位数更高。 糖尿病患者的分布范围更广。 血糖 明显区别:糖尿病患者的血糖水平明显更高。 在该数据集中,葡萄糖是一个强有力的指标--IQR 的重叠极少。 两组中都有异常值,但非糖尿病组的异常值更大。 血压 中位数相当接近,但糖尿病患者的数值略高。 非糖尿病组的异常值较低。 总体而言,血压可能不是一个强有力的区分指标。 皮肤厚度 糖尿病患者和非糖尿病患者之间的差异很微妙。 两组的中位数和均方根值相似。 糖尿病患者的几个异常值延伸得更远,但这一特征似乎不那么具有决定性。 胰岛素 两组的差异都很大,都有很多异常值。 糖尿病患者的中位数似乎略高,但重叠程度较大。 体重指数 糖尿病患者的体重指数普遍较高。 两组之间的中位数有明显变化。 分布略偏右,有一些异常值,但这是一个有用的特征。 糖尿病谱系系数 糖尿病组的平均值较高。 糖尿病患者中有许多高值异常值,表明遗传风险更大。 虽然分布有所重叠,但这一特征有助于预测。 年龄 糖尿病患者往往年龄较大。 中位数和 IQRs 有明显差异,糖尿病组的高端值更多。 这一特征似乎非常相关。 皮马印第安人数据集中的结果分布 从上图可以看出,我们有 500 名非糖尿病患者和 268 名糖尿病患者。这使得分布不平衡(即数据集中的目标变量或类别没有被平等地表示出来),如果处理不当,可能会导致潜在的偏差或泛化能力差。
每个属性的平均分布(按结果着色) 上方的条形图显示了每个数值特征的平均值,并以 Outcome
变量(指示一个人是否患有糖尿病)分隔开。这有助于理解两组之间每个健康指标的平均值有何差异。我们注意到,糖尿病
患者和非糖尿病患者的胰岛素、血糖、年龄和BMI水平之间存在明显的一致性。这表明上述指标是患者患糖尿病的明确指标。妊娠期的一致性表明,有过多次妊娠的女性患妊娠期糖尿病的风险更高。
从非对角线(散点图)中发现:
葡萄糖似乎是一个非常重要的特征。在大多数涉及葡萄糖的散点图中,两个结果类别有明显的分离,橙色/红色(可能是 “糖尿病”)点一般出现在葡萄糖值较高的地方 葡萄糖与胰岛素:存在正相关。随着葡萄糖的增加,胰岛素也会增加。糖尿病 "组通常显示较高的葡萄糖和胰岛素值。 葡萄糖与体重指数: 呈正相关。较高的葡萄糖通常与较高的体重指数相关。糖尿病 "组的血糖和体重指数通常较高。 葡萄糖与年龄的关系:总体趋势是,葡萄糖水平越高,年龄就越大,尤其是 “糖尿病” 结果。 体重指数与血压:存在正相关关系,体重指数越高,血压越高。 怀孕次数与年龄:存在正相关,正如预期的那样,年龄越大的人怀孕次数越多。 胰岛素及其分布:涉及胰岛素的散点图突显了其奇特的分布。许多点聚集在零点附近,这是用零值计算的缺失数据。对于非零胰岛素值,与葡萄糖呈正相关,这表明随着葡萄糖水平的升高,人体会分泌更多的胰岛素来控制葡萄糖水平,但对于糖尿病患者来说,这种反应可能不足或无效。 皮肤厚度与体重指数: 皮肤厚度与体重指数之间有很强的正相关性,这在生理学上是意料之中的,因为两者都与身体脂肪有关。 血压及其影响:虽然血压显示了一些趋势,但它本身似乎并不像葡萄糖或体重指数那样具有强烈的区分作用。 相关性热图 上图给出了属性之间的相关性矩阵,范围从 -1(强负相关性)、0(无相关性)和+1(强正相关性)。
与结果的相关性:
Glucose
和 Outcome
之间存在中等强度的正相关性。这是完全可以预料到的,因为高血糖水平是糖尿病的主要指标。这表明,随着血糖水平的升高,出现积极结果(糖尿病)的可能性也会增大。 BMI
和 Outcome
之间存在中等强度的正相关性。 Age
和 Outcome
之间存在微弱的正相关性。患糖尿病的风险通常会随着年龄的增长而增加。 Pregnancies
和 Outcome
之间存在微弱的正相关性,这可能与妊娠期糖尿病或多胎妊娠可能与以后患 2 型糖尿病的风险较高有关。
# 根据可靠的健康分类分层,对年龄、BMI、血压进行分类属性 # 本项目的数据集和代码获取:@公众号:数据STUDIO 原文《机器学习实战:糖尿病预测分析及可视化》 文末打赏任意金额便可获取 df[ 'AgeGroup' ] = pd.cut(df[ 'Age' ], bins=[ 20 , 30 , 40 , 50 , 60 , 100 ], labels= [ 'Young Adult' , 'Early Middle Age' , 'Late Middle Age' , 'Early Senior' , 'Senior' ]) df[ 'BMI_Group' ] = pd.cut(df[ 'BMI' ], bins=[ 0 , 18.5 , 25 , 30 , 100 ], labels=[ 'Underweight' , 'Normal' , 'Overweight' , 'Obese' ]) df[ 'BloodPressure_Group' ] = pd.cut(df[ 'BloodPressure' ], bins=[ 0 , 60 , 80 , 90 , 120 , 200 ], labels=[ 'Low' , 'Normal' , 'Pre-hypertension' , 'Stage 1 Hypertension' , 'Stage 2 Hypertension' ]) df[ 'Outcome_Group' ] = df[ 'Outcome' ].map({ 0 : 'No Diabetes' , 1 : 'Diabetes' }) df[ 'Glucose_Category' ] = pd.cut(df[ 'Glucose' ], bins=[ 0 , 140 , 200 ], labels=[ 'Normal' , 'Impaired glucose tolerance' ]) df[[ 'Glucose' , 'Glucose_Category' ]] # 根据提供的范围定义胰岛素类别 df[ 'Insulin_Category' ] = pd.cut(df[ 'Insulin' ], bins=[ 0 , 30 , 100 , 150 , 1000 ], # Increased upper bound to include high values labels=[ '< 30 (Possible Deficiency)' , '30-100 (Normal)' , '100-150 (Early Resistance)' , '> 150 (Significant Resistance)' ], right= False ) # 使用 right=False 使箱体不包括右边缘,与描述相匹配 # 定义 DiabetesPedigreeFunction 的箱 bins = [ 0 , 0.5 , 1.0 , 1.5 , df[ 'DiabetesPedigreeFunction' ].max()]
labels = [ '0-0.5' , '0.5-1.0' , '1.0-1.5' , '>1.5' ] # 为 DPF 箱体创建新列 df[ 'Pedigree_Bin' ] = pd.cut(df[ 'DiabetesPedigreeFunction' ], bins=bins, labels=labels, right= False )
为了增强可解释性并揭示数据中更深层次的模式,我根据既定的医疗保健分层,将几个连续的健康指标转化为具有临床意义的类别。这一步骤可以实现更直观的分析和可视化,尤其是在比较不同人群的糖尿病结果时。
Age
按照生命阶段分为以下几个组:青年(21-30 岁)、中年早期(31-40 岁)、中年晚期(41-50 岁)、老年早期(51-60 岁)和老年(61 岁以上)。 BMI
使用 WHO 标准分类对值进行分组:体重过轻(<18.5)、正常(18.5–24.9)、超重(25–29.9)和肥胖(30+)。 根据 Blood Pressure
舒张压截断值,将血压分为临床范围:低血压、正常血压、高血压前期和1-2 期高血压。 二元变量 Outcome
被映射到更易读的标签——糖尿病和非糖尿病,以便图表和表格更加清晰。 糖耐量受损(140-199mg/dL)。 这些阈值反映了 2 小时口服葡萄糖耐量测试 (OGTT) 中使用的标准指南,有助于识别可能处于糖尿病前期的个体。 根据
Insulin
胰岛素敏感性和抵抗性的临床解释,将水平分为四类: 30–100 μU/mL — 正常2小时血清胰岛素反应 100–150 μU/mL — 胰岛素抵抗的早期迹象 150 μU/mL — 严重的胰岛素抵抗。 定义的分箱既反映了诊断范围,也反映了数据集中观察到的分布。使用 right=False
确保每个类别的上限不重叠 虽然这 DiabetesPedigreeFunction
并非传统意义上的临床指标,但它有助于根据家族史了解患者的糖尿病遗传易感性。为了更直观地分析这一特征,我将连续的DPF值划分为四个分类箱。这些分类箱有助于突出显示遗传风险的增加与数据集中糖尿病患病率之间的关联。 现在,进入分类图表:
按结果对葡萄糖进行分类 这张堆叠条形图显示了两组血糖类别中糖尿病阳性和阴性结果的百分比。它告诉我们,血糖受损的糖尿病患者比例要高得多。因此,这再次印证了我们的假设:血糖是最终糖尿病结果的关键决定因素。所有年龄段的人都应定期进行血糖检测,即使患者尚未患糖尿病。
年龄分布——饼图 从两个饼图来看,老年人在我们的数据集中没有得到充分体现,因此我们可以对这个年龄段的分析持保留态度。
结果分布——年龄组 这张水平条形图展示了各年龄段糖尿病状况的分布情况。由此可见,患糖尿病的风险较高的人群是中年早期(31-40岁)和老年早期(51-60岁)。因此,强烈建议在40岁这个门槛上进行早期检测和监测。
结果分布 — bmi group 由此可见,BMI 肥胖人群最容易患糖尿病。因此,对患者采取积极的体重管理策略是最可行的解决方案。
结果分布——分级妊娠 妊娠次数与糖尿病患病率之间似乎存在某种关联。随着妊娠次数的增加,确诊患有糖尿病的女性比例也显著增加。这进一步表明,多次妊娠的女性患妊娠期糖尿病的风险可能更大。
结果分布——dpf 从上面的多条形图中可以看出,在谱系功能较高的人群中,糖尿病患者的相对比例往往较高。这也表明遗传易感性可能是一个影响因素。
仅有一次观察结果显示患有 2 期高血压(数据不足) 从我理解数据的背景来看,糖尿病患者患高血压的可能性是普通人的两倍。这更像是糖尿病的后遗症,而非病因。糖尿病会损伤肾脏,导致水盐潴留,进而导致血压升高。因此,糖尿病对高血压前期和一期高血压患者的影响更为显著。
结果分布——胰岛素 胰岛素抵抗通常是血糖水平受损的先兆。这意味着抵抗力显著的个体最容易患糖尿病,超过一半的严重抵抗女性患有糖尿病。这个问题可以通过定期检测患者的胰岛素水平来解决。100mU/mL 应作为进一步监测和采取早期预防措施的标志。
结果分布——胰岛素和葡萄糖 由于血糖和胰岛素是手动设置的属性,我们使用了屏蔽函数、运算符和位置寻址来整合先前创建的
Glucose_Category
和 Insulin_Category
列中的信息。这可以根据患者的血糖和胰岛素水平识别特定的亚组,并仅关注属于这些定义的高血糖和胰岛素抵抗组的患者。
这里的数字高得惊人,如果病人的血糖水平处于这些高值范围之间,那么他们患糖尿病的可能性就很大
特征预处理 完成整个 EDA 后,需要准备用于建模的数据。首先,我们准备用于训练机器学习模型的特征(自变量)。在监督学习中,我们将数据集分为特征(用于进行预测的数据)和目标变量(我们想要预测的内容)。
X = df.drop(columns=df.select_dtypes(exclude=np.number)) X.drop(columns=[ 'Outcome' ], inplace=True) X y = df[ 'Outcome' ] y
特征相关性 独立特征之间的相关性: Glucose
和 Insulin
之间存在相对较强的正相关性。这在生物学上是合理的,因为胰岛素会根据血糖水平释放以调节血糖水平。 SkinThickness
和 BMI
是独立特征之间最强的相关性之一。这很合理,因为皮褶厚度测量值通常用于计算或与 BMI
相关。 Pregnancies
和 Age
也暗示了强烈的正相关性。这是合乎逻辑的,因为女性随着年龄的增长通常会怀孕更多次。
弱/负相关性:注意一些非常弱或接近于零的相关性,例如 BloodPressure
和 Insulin
(0.045)或 DiabetesPedigreeFunction
和 BloodPressure
(-0.0024)。这些低值表明这些特征在很大程度上以线性方式相互独立(例如
Pregnancies
和 DiabetesPedigreeFunction
(0.034))。
特征缩放 scaler = StandardScaler() scaledX = scaler.fit_transform(X)
标准化是机器学习中常见的预处理步骤,旨在确保值较大的特征不会对模型造成不成比例的影响。这可以通过将数据变换为平均值为 0、标准差为 1 来实现。
特征指标 理解混淆矩阵 再次,你还记得我们说过这个数据集不平衡吗?你的模型可能倾向于多数类,即使你获得了很高的准确率,也可能在关键的地方表现不佳。95% 的准确率可能掩盖了模型从未正确预测少数类(重要的类)的事实 —— 而当它真正重要时,这可能会带来灾难性的后果。因此,我们不应该关注准确率,而是应该评估这些模型的指标: 精度、准确率、F-1 分数和 AUC(曲线下面积)。
精确度是:“模型每次说‘糖尿病’时,有多少人实际上是糖尿病患者?”——如果你的模型说有 10 个人是糖尿病患者,但实际上只有 6 个人是糖尿病患者,那么你的精确度就是 0.6(非糖尿病患者也是如此)
召回率是: “在所有实际的糖尿病病例中,模型捕获了多少个?”——这对于非糖尿病类别也存在)
F1 分数是: “精确度和召回率之间的平衡点在哪里?”——这通常是不平衡数据集的最佳指标。它是精确度和召回率的调和平均值。为了获得较高的 F1 分数,你的召回率和精确度也必须很高。
AUC 是 : “模型能多好地区分类别(例如,是与否)?”——这个值介于 0 到 1 之间。这往往会对模型的整体性能进行排名
训练-测试拆分验证 为了评估机器学习模型的性能,必须将数据集拆分为两部分:用于训练模型的训练集,以及用于评估模型在未知数据上表现的测试集。我们将数据集的 20% 分配给测试集,80% 分配给训练集。我们使用 Scikit-learn 的训练-测试拆分功能来执行此操作。
X_train,X_test,y_train,y_test = train_test_split(scaledX,y,test_size= 0.2,random_state= 42)
模型训练 因为这是一个分类模型,所以我们将使用决策树、随机森林等分类模型。
我们从逻辑回归开始:
# 训练模型以使其根据输入进行预测 log = LogisticRegression() log.fit(X_train, y_train) # 使用训练好的对数模型对测试数据集进行预测 log_preds = log.predict(X_test)
评估:逻辑回归的准确度评分和分类报告
随机森林分类 现在我们尝试随机森林
forest = RandomForestClassifier(random_state= 56 ) #为了便于复现 forest.fit(X_train, y_train) #训练模型 forest_preds = forest.predict(X_test) #在未见过的数据上测试模型
评估:随机森林的准确率得分和分类报告 决策树分类器 接下来是决策树:
#初始化决策树分类器 tree = DecisionTreeClassifier(random_state= 56 ) # 将模型拟合到训练数据 tree.fit(X_train, y_train) # 对测试数据进行预测 tree_preds = tree.predict(X_test)
评估:决策树的准确度得分和分类报告 KNN分类器 K最近邻分类器下一步:
# 初始化 KNN 分类器 knn = KNeighborsClassifier(n_neighbors= 25 ) # 您可以选择邻居的数量(k) # 将模型拟合到训练数据 knn.fit(X_train, y_train) # 对测试数据进行预测 knn_preds = knn.predict(X_test)
评估:K最近邻分类器的准确率和分类报告 XGBoost分类器 # 创建 XGBoost 分类器 xgb_model = xgb.XGBClassifier(random_state= 42 ) # 训练模型 xgb_model.fit(X_train, y_train) # 进行预测 xgb_preds = xgb_model.predict(X_test)
评估:xgbclassifier 的准确率和分类报告 不同模型ROC对比 我们对比了多个模型训练效果如图所示:
超参数调整 我们对随机森林和决策树模型执行了 GridSearch 和 RandomizedSearchCV,希望在评估期间获得更好的 F1 分数。
以下是所有模型的最终表现:
模型性能 调整随机森林(随机搜索)在少数结果中表现最佳,而 KNN 在多数结果中表现最佳。我们选择部署调整随机森林来测试我们的虚拟数据,但这还有待观察。
这是所有使用的模型的 2*4 混淆矩阵
特征重要性
这有助于我们对每个模型在训练和测试中使用的属性进行排名,以确定或选择类别。
特征重要性 由此可以肯定的是:血糖是预测患者糖尿病的首要决定因素。BMI、年龄和胰岛素也是预测结果的关键属性。
测试一下:
特征气泡图 你看到这张气泡图了吗?它告诉我们,在血糖和BMI水平较高时,有一簇大的橙色气泡(结果 = 1),这表明BMI和血糖值较高的老年人更有可能获得积极的结果。你会注意到,许多大的紫色圆圈位于血糖轴的下方。这意味着他们可能年龄较大、体重较重——但他们的血糖水平不高,所以他们不是糖尿病患者。
大橙色(上图) = 高 BMI + 高血糖 + 年龄较大 = 高风险 大紫色(下图) = 可能年纪大或 BMI 高,但血糖正常或有其他保护因素 胰岛素抵抗气泡图 最上象限的圆圈大多巨大且呈橙色。这告诉我们,胰岛素抵抗和血糖升高(见上图)的情况不容乐观。这几乎肯定会导致糖尿病,或者至少是定时炸弹。这里大多数负面结果的血糖和胰岛素水平都正常。
现在,令人瞠目结舌的事情发生了!因为我喜欢风险分析,所以我们喜欢用几率来衡量概率。我们建立了一个简单的风险评分系统,如果BMI > 30,则+1;如果血糖 > 140,则+1;如果年龄 > 40,则+1;如果胰岛素 > 150,则+1;如果怀孕次数 > 3,则+1。我们计算了每个分数对应的糖尿病发病率,并将其列在0-1范围内的热图上(根据符合的属性,分数越接近1,患者患糖尿病的风险就越大)。
# 将基于最重要特征的风险评分逻辑应用于 DataFrame def calculate_risk_score ( row ) : score = 0 # 初始化为 0 并按条件递增 if row[ 'BMI' ] > 30 : score += 1 if row[ 'Glucose' ] > 140 : score += 1 if row[ 'Age' ] > 40 : score += 1 if row[ 'Insulin' ] >
150 : score += 1 if row[ 'Pregnancies' ] > 3 : score += 1 return score df[ 'RiskScore' ] = df.apply(calculate_risk_score, axis= 1 ) # 计算每个风险评分的糖尿病发病率 risk_diabetes_rate = df.groupby( 'RiskScore' )[ 'Outcome' ].mean().reset_index() # 创建热图 plt.figure(figsize=( 10 , 6 )) sns.heatmap(risk_diabetes_rate.set_index( 'RiskScore' ).T, annot= True , cmap= 'Reds' , fmt= ".2f" , linewidths= .5 ) plt.title( 'Diabetes Rate vs. Risk Score Heatmap' ) plt.xlabel( 'Risk Score' ) plt.ylabel( 'Outcome (1=Diabetes)' ) plt.yticks([]) # 隐藏 y 轴标签,因为它只有'结果 (1=糖尿病)' plt.show()
风险评分热图 即使只超过 3 个阈值,患者也或多或少有可能患有糖尿病。超过 4 个阈值是一个巨大的灾难性危险信号,我们希望迅速找到干预措施,而超过 5 个阈值几乎肯定是糖尿病
测试虚拟数据 我们将一个虚拟数据集传递给模型,以测试该患者是阳性还是阴性。我们将数据以数组的形式传递,顺序与数据字典中的顺序相同,然后缩放数据,使用部署的模型执行预测,并评估两种结果的确定性概率。请看下面的代码:
# 示例患者数据作为 NumPy 数组 # 确保特征的顺序与训练数据匹配 # 训练数据 (X) 中的特征为: # ['Pregnancies', 'Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'DiabetesPedigreeFunction', 'Age'] # 示例患者数据(用实际患者数据替换) # 这只是一个包含 8 个值的占位符数组。 # 您需要在此处提供实际患者数据。 patient_data_array = np.array([[ 2 , 130 , 70 , 30 , 128 , 35.0 , 0.82 , 43 ]]) # 使用与训练数据相同的缩放器缩放患者数据 scaled_patient_data = scaler.transform(patient_data_array) # 使用随机搜索模型执行预测 prediction = best_tree_model.predict(scaled_patient_data) # 预测结果是一个 numpy 数组,访问第一个元素 prediction_result = prediction[ 0 ] # 解释预测 if
prediction_result == 1 : print( "Prediction: The patient is likely to have diabetes." ) else : print( "Prediction: The patient is likely not to have diabetes." ) # 获取每个类的概率 prediction_proba = best_rs_forest_model.predict_proba(scaled_patient_data) print( f"Probability of no diabetes (Class 0): {prediction_proba[ 0 ][ 0 ]: .4 f} " ) print( f"Probability of diabetes (Class 1): {prediction_proba[ 0 ][ 1 ]: .4 f} " )
根据虚拟数据进行预测 预计这位患者最有可能患有糖尿病——我叫她 Anaya
这是一个显示 Anaya 的用户资料及其病史的仪表板
anaya 的个人资料 我们还做了一个交互式的,要求用户输入。我们收集了用户(例如患者)的医疗输入,对输入进行预处理,使其与经过训练的机器学习模型(例如本例中的 KNN)使用的格式相匹配,预测患者是否可能患有糖尿病,并显示结果的概率和解释。
我们利用 try-except 验证块来确保数据 float
仅在数据类型中被接受并附加到列表数组中 patient_data
# 本项目的数据集和代码获取:@公众号:数据STUDIO 原文《机器学习实战:糖尿病预测分析及可视化》 文末打赏任意金额便可获取 # 允许用户输入进行预测 def get_patient_input () : """从用户那里获取患者属性作为输入并进行验证。""" feature_names = [ 'Pregnancies' ,
'Glucose' , 'BloodPressure' , 'SkinThickness' , 'Insulin' , 'BMI' , 'DiabetesPedigreeFunction' , 'Age' ] patient_data = [] print( "Please enter the patient's details:" ) for feature in feature_names: while True : try : value = float(input( f"Enter {feature} : " )) # 添加基本验证(例如,非负值) if feature in [ 'Pregnancies' , 'Age' ] and value 0 : print( "Value cannot be negative. Please enter again." ) continue if feature in [ 'Glucose' , 'BloodPressure' , 'SkinThickness' , 'Insulin' , 'BMI' ] and value 0 : print( "Value cannot be negative. Please enter again." ) continue if feature == 'DiabetesPedigreeFunction' and value 0 : print( "Value cannot be negative. Please enter again." ) continue patient_data.append(value) break # 如果输入有效,则退出循环 except ValueError: print( "Invalid input. Please enter a numerical value." ) except Exception as e: print( f"An error occurred: {e} " )) return np.array([patient_data]) # 从用户那里获取输入 user_patient_data = get_patient_input() # 使用在训练数据上适合的相同缩放器缩放用户输入数据 scaled_user_data = scaler。transform(user_patient_data) user_prediction = knn.predict(scaled_user_data) # 解释预测 user_prediction_result = user_prediction[ 0 ] if user_prediction_result == 1 : print( "\nPrediction: Based on the input data, the patient is likely to have diabetes." ) else : print( "\nPrediction: Based on the input data, the patient is likely not to have diabetes." ) print( "\nPrediction: Based on the input data, the patient is likely not to have diabetes." ) # 获取每个类的概率 user_prediction_proba = knn.predict_proba(scaled_user_data) print( f"Probability of no diabetes (Class 0): {user_prediction_proba[ 0 ][ 0 ]: .4 f} " ) print( f"Probability of diabetes (Class 1): {user_prediction_proba[
0 ][ 1 ]: .4 f} " )
用户输入——预测 在这里,你可以看到,即使它预测患者可能患有糖尿病,也并不像第一个例子那样确定。所以,谁知道呢?这可能是假阳性(也可能不是)。
当我们继续探索健康与机器学习的交集时,记住:每个数据点不仅仅是一个统计数据;它代表着一个人类的故事。