热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

python数据分析之用sklearn预测糖尿病_python

这篇文章主要介绍了python数据分析之用sklearn预测糖尿病,文中有非常详细的代码示例,对正在学习python数据分析的小伙伴们有很好地帮助,

一、数据集描述

本数据集内含十个属性列

Pergnancies: 怀孕次数

Glucose:血糖浓度

BloodPressure:舒张压(毫米汞柱)

SkinThickness:肱三头肌皮肤褶皱厚度(毫米)

Insulin:两个小时血清胰岛素(μU/毫升)

BMI:身体质量指数,体重除以身高的平方

Diabets Pedigree Function: 疾病血统指数

是否和遗传相关,Height:身高(厘米)

Age:年龄

Outcome:0表示不患病,1表示患病。

任务:建立机器学习模型以准确预测数据集中的患者是否患有糖尿病

二、准备工作

查阅资料得知各属性的数据值要求,方面后期对于数据的分析与处理过程。

属性列名称 数据值要求

Pergnancies(怀孕次数) 符合常理即可(可为0)

Glucose(血糖浓度) 正常值为:80~120

BloodPressure(舒张压(毫米汞柱)) 正常值为:60~80

SkinThickness(肱三头肌皮肤褶皱厚度(毫米)) 不为0

Insulin(两个小时血清胰岛素(/毫升)) 正常值为:35~145

BMI(身体质量指数:体重除以身高的平方) 正常值为:18.5~24.9

Diabets Pedigree Function:(疾病血统指数:是否和遗传相关) 无特殊值要求

Height(身高(厘米)) 不为0 符合常理即可

Age(年龄) 符合常理即可

Outcome(0表示不患病,1表示患病) 标签值

三、实验环境和工具

python3.5.6 + jupyter

数据处理 pandas、numpy

可视化 matplotlib、seaborn

模型构建 sklearn

四、预测分析

4.1探索性数据分析

 数据描述

首先观察基本的数据类型,以及数据是否存在缺失情况,简要统计信息

all_data.shape
all_data.info()

RangeIndex: 768 entries, 0 to 767
Data columns (total 10 columns):
 #   Column                    Non-Null Count  Dtype  
---  ------                    --------------  -----  
 0   Pregnancies               768 non-null    int64  
 1   Glucose                   768 non-null    int64  
 2   BloodPressure             768 non-null    int64  
 3   SkinThickness             768 non-null    int64  
 4   Insulin                   768 non-null    int64  
 5   BMI                       768 non-null    float64
 6   DiabetesPedigreeFunction  768 non-null    float64
 7   Age                       768 non-null    int64  
 8   Height                    766 non-null    object 
 9   Outcome                   768 non-null    int64  
dtypes: float64(2), int64(7), object(1)
memory usage: 60.1+ KB

数据总量时比较少的只有768个例子,可以看到除Height外的属性都为数值型属性。在后续数据预处理过程需要对Height属性进行类型转换操作。目前没有缺失值的出现。

# height 数值类型 为object 需要转化为 数值型
all_data = all_data.astype({'Height':'float64'})
all_data.describe()

在这里插入图片描述

发现两个问题:

1.缺失值

从其中的min值可以很直观地观察到,Glucose, BloodPressure, SkinTinckness, Insulin, BMI等特征存在0值的情况(当然Pregnancies根据常识判断是可以为0的)。而根据常规范围明显可以判定这些0值是不合理的,所以也是一种缺失值缺失值,后续数据预处理需要对这些缺失值进行填充处理。

2.离群值/异常值

Glucose,BloodPressure,SkinTinckness,Insulin等特征的max值和75%分位点值或者min值和25%分位点值之间的差距比较大,初步判断可能存在离群值/异常值。尤其是SkinThickness和Insulin特征(具体见图4红色框部分),后续可以通过可视化进一步直观地观察判断。

为了方便后序对缺失值的统一处理,将异常值统一替换为np.nan。

import numpy as np
#缺失值替换 经分析,除怀孕次数,其他特征的0值表示缺失值 替换为np.nan
replace_list = ['Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'Height']
all_data.loc[:,replace_list] = all_data.loc[:,replace_list].replace({0:np.nan})
#各特征缺失数量统计
null_count = all_data.isnull().sum().values
# 缺失值情况
plt.figure()
sns.barplot(x = null_count, y = all_data.columns)
for x, y in enumerate(null_count):
    plt.text(y, x, "%s" %y, horizOntalalignment='center', verticalalignment='center')
plt.show()

在这里插入图片描述

可以观察到Glucose,Insulin,SkinThickness,BMI,Height等特征都存在缺失值。并且 Insulin,SkinThickness缺失值比较多,分别占到了48%,30%的比例。所以后期数据预处理也是很关键的。

五、可视化分析

接下来通过更多针对性的可视化,来进一步探索特征值的分布以及特征和预测变量之间的关系

# 患病和不患病情况下 箱线图查看数据分散情况
for col in all_data.columns:
    plt.figure(figsize = (10,6))
    if all_data[col].unique().shape[0] > 2:
        sns.boxplot(x="Outcome", y=col, data=all_data.dropna())
    else:
        sns.countplot(col,hue = 'Outcome',data = all_data.dropna())
    plt.title(col)
    plt.show()

部分输出:

在这里插入图片描述

在这里插入图片描述

观察患病和不患病情况下 各特征值或者人数分布
label接近2:1 存在一定的分布不平衡 
像insulin之类的特征离群值是比较多的,由于离群值会对模型评估产生影响,所以后续可能要做处理,剔除偏离较大的离群值
# 患病和不患病情况下 各特征的分布情况
for col in all_data.drop('Outcome',1).columns:
    plt.figure()
    sns.displot(data = all_data, x = col,hue = 'Outcome',kind='kde')
    plt.show()

部分输出:

在这里插入图片描述


在这里插入图片描述

1.从数据样本本身出发研究数据分布特征,可以发现在患病和不患病两种情况下,部分特征的密度分布比较相近,特别是height的分布图,发现两曲线基本相近。感觉和label之间的相关性都不是很强。

2.同时,可以发现部分特征存在右偏的现象(skewness (偏度) 描述数据分布形态的统计量,其描述的是某总体取值分布的对称性),考虑到需要数据尽量服从正态分布,所以后续数据预处理需要对存在一定偏度的特征进行相关处理。

# 观察各特征分布和患病的关系
corr = all_data.corr()
plt.figure(figsize = (8,6))
sns.heatmap(corr,annot = True,cmap = 'Blues')
plt.show()

在这里插入图片描述

heatmap()函数可以直观地将数据值的大小以定义的颜色深浅表示出来。

1.可以发现颜色相对来说都比较浅,也就是说无论是特征和特征之间还是特征和outcome标签之间的相关性都没有很高。

2.发现其余各特征变量中与outcome的相关度中最高的是Glucose 属性值为0.49,最低的是Height属性值为0.059。

3.同时观察特征与特征之间的关系,发现Insulin与Glucose,BMI和SkinThickness之间的相关度分别为0.58,0.65属于比较高的相关性,由于Insulin是一个确实比较严重的特征,而相关性可以是一种协助填充缺失值的方法。

plt.figure()
sns.scatterplot(x = 'Insulin', y = 'Glucose', data = all_data)
plt.show()
sns.scatterplot(x = 'Insulin', y = 'BMI', data = all_data)
plt.show()
sns.scatterplot(x = 'Insulin', y = 'Age', data = all_data)
plt.show()

plt.figure()
sns.scatterplot(x = 'SkinThickness', y = 'BMI', data = all_data)
plt.show()
sns.scatterplot(x = 'SkinThickness', y = 'Glucose', data = all_data)
plt.show()
sns.scatterplot(x = 'SkinThickness', y = 'BloodPressure', data = all_data)
plt.show()

部分输出:

在这里插入图片描述
在这里插入图片描述

六、构建baseline

因为决策树几乎不需要数据预处理。其他方法经常需要数据标准化,创建虚拟变量和删除缺失值。

# 读取数据
all_data = pd.read_csv('data.csv')

# height 数值类型 为object 需要转化为 数值型
all_data = all_data.astype({'Height':'float64'})
# 
all_data.dropna(inplace = True)
# 特征
feature_data = all_data.drop('Outcome',1)
# 标签
label = all_data['Outcome']

base_model = DecisionTreeClassifier()
base_scores = cross_validate(base_model, feature_data, label,cv=5,return_train_score=True)
print(base_scores['test_score'].mean())

0.6954248366013072

七、数据预处理

综合前面分析,先做了以下处理

# 读取数据
all_data = pd.read_csv('data.csv')

# height 数值类型 为object 需要转化为 数值型
all_data = all_data.astype({'Height':'float64'})

# 理论缺失值0替换为np.nan
replace_list = ['Glucose', 'BloodPressure', 'SkinThickness', 'Insulin', 'BMI', 'Height']
all_data.loc[:,replace_list] = all_data.loc[:,replace_list].replace({0:np.nan})

# 删除相关性低的Height
all_data.drop('Height',1,inplace = True)

八、离群值处理

1.经过前面的分析发现数据是存在部分离群值的,虽然实验本身就是关于疾病预测,异常值的存在属于正常现象。但是对于一些可能超出人体接受范围的值,衡量对预测的影响之后,由于数据量比较小,这里选择删除极端异常点。

2.极端异常点 :上限的计算公式为Q3+3(Q3-Q1) 下界的计算公式为Q1-3(Q3-Q1))。

# remove the outliers
# 异常点 上须的计算公式为Q3+1.5(Q3-Q1);下须的计算公式为Q1-1.5(Q3-Q1)
# 极端异常点 :上限的计算公式为Q3+3(Q3-Q1) 下界的计算公式为Q1-3(Q3-Q1)
# 由于数据量比较少 所以选择删除极端异常值
def remove_outliers(feature,all_data):
    first_quartile = all_data[feature].describe()['25%']
    third_quartile = all_data[feature].describe()['75%']
    iqr = third_quartile - first_quartile
    # 异常值下标
    index = all_data[(all_data[feature] <(first_quartile - 3*iqr)) | (all_data[feature] > (first_quartile + 3*iqr))].index
    all_data = all_data.drop(index)
    return all_data
outlier_features = ['Insulin', 'Glucose', 'BloodPressure', 'SkinThickness', 'BMI', 'DiabetesPedigreeFunction']
for feat in outlier_features:
    all_data = remove_outliers(feat,all_data)

处理之后的数据基本的统计信息

在这里插入图片描述

九、缺失值处理

1.直接删除处理

def drop_method(all_data):
    median_fill = ['Glucose', 'BloodPressure','SkinThickness', 'BMI','Height']
    for column in median_fill:
        median_val = all_data[column].median()
        all_data[column].fillna(median_val, inplace=True)
    all_data.dropna(inplace = True)
    return all_data

2.中值填充

def median_method():
    for column in list(all_data.columns[all_data.isnull().sum() > 0]):
        median = all_data[column].median()
        all_data[column].fillna(median, inplace=True)

3.KNNImputer填充

def knn_method():
    # 先将缺失值比较少的特征用中值填充
    values = {'Glucose': all_data['Glucose'].median(),'BloodPressure':all_data['BloodPressure'].median(),'BMI':all_data['BMI'].median()}
    all_data.fillna(value=values,inplace=True)

    # 用KNNImputer 填充 Insulin SkinThickness
    corr_SkinThickness = ['BMI', 'Glucose','BloodPressure', 'SkinThickness']
    # 权重按距离的倒数表示。在这种情况下,查询点的近邻比远处的近邻具有更大的影响力
    SkinThickness_imputer = KNNImputer(weights = 'distance')
    all_data[corr_SkinThickness] = SkinThickness_imputer.fit_transform(all_data[corr_SkinThickness])

    corr_Insulin = ['Glucose', 'BMI','BloodPressure', 'Insulin']
    Insulin_imputer = KNNImputer(weights = 'distance')
    all_data[corr_Insulin] = Insulin_imputer.fit_transform(all_data[corr_Insulin])

4.随机森林填充

from sklearn.ensemble import RandomForestRegressor
from sklearn.impute import SimpleImputer  # 用来填补缺失值
def predict_method(feature):
    # 复制一份数据 避免对原数据做出不必要的修改
    copy_data = all_data.copy()
    # 缺失了的下标
    predict_index = copy_data[copy_data[feature].isnull()].index
    # 没缺失的下标
    train_index = copy_data[feature].dropna().index
    # 用作预测 的训练集标签
    train_label = copy_data.loc[train_index,feature]
    copy_data = copy_data.drop(feature,axis=1)
    # 对特征先用中值填充
    imp_median = SimpleImputer(strategy='median')
    # 用作预测的训练集特征
    train_feature = copy_data.loc[train_index]
    train_feature = imp_median.fit_transform(train_feature)
    # 需要进行预测填充处理的缺失值
    pre_feature = copy_data.loc[predict_index]
    pre_feature = imp_median.fit_transform(pre_feature)
    # 选取随机森林模型
    fill_model = RandomForestRegressor()
    fill_model = fill_model.fit(train_feature,train_label)
    # 预测 填充
    pre_value = fill_model.predict(pre_feature)
    all_data.loc[predict_index,feature] = pre_value

#用随机森林的方法填充缺失值较多的 SkinThickness 和 Insulin 缺失值
predict_method("Insulin")
predict_method("SkinThickness")
# 其余值中值填充
for column in list(all_data.columns[all_data.isnull().sum() > 0]):
    median = all_data[column].median()
    all_data[column].fillna(median, inplace=True)

十、特征工程

# 特征
feture_data = all_data.drop('Outcome',1)
# 标签
label = all_data['Outcome']
# 利用BMI和身高构造weight特征
# BMI = weight(kg) / height(m)**2
feture_data['weight'] = (0.01*feture_data['Height'])**2 * feture_data['BMI']

十一、数据标准化

# 标准化
Std = StandardScaler()
feture_data = Std.fit_transform(feture_data)

十二、模型构建与调参优化

用到的模型

from sklearn.svm import SVC,SVR
from sklearn.tree import DecisionTreeClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.ensemble import RandomForestClassifier,StackingClassifier

调参方法

from sklearn.model_selection import GridSearchCV

评估指标 Accuracy roc_auc

from sklearn.metrics import make_scorer from sklearn.metrics import
accuracy_score from sklearn.metrics import roc_auc_score

def train(model, params):
    grid_search = GridSearchCV(estimator = model, param_grid = params,scoring=scores,refit='Accuracy')
    grid_search.fit(feture_data,label)
    print(grid_search.best_estimator_)
    return grid_search

def paint(x,y):
    plt.figure()
    sns.lineplot(x=x,y=y)
    plt.show()

SVC

#调参时先尝试一个大范围,确定比较小的范围,然后在小范围里搜索
model = SVC()
params  =  {'C':np.linspace(0.1, 2, 100)}
SVC_grid_search = train(model,params)
                        
paint([x for x in range(100)],SVC_grid_search.cv_results_['mean_test_Accuracy'])
paint([x for x in range(100)],SVC_grid_search.cv_results_['mean_test_AUC'])
print("test_Accuracy : {}\ntest_AUC : {}".format(SVC_grid_search.cv_results_['mean_test_Accuracy'].mean(),SVC_grid_search.cv_results_['mean_test_AUC'].mean()))

LogisticRegression

model = LogisticRegression()
params = {"C":np.linspace(0.1,2,100)}
LR_grid_search = train(model,params)
                        
paint([x for x in range(100)],LR_grid_search.cv_results_['mean_test_Accuracy'])
paint([x for x in range(100)],LR_grid_search.cv_results_['mean_test_AUC'])
print("test_Accuracy : {}\ntest_AUC : {}".format(LR_grid_search.cv_results_['mean_test_Accuracy'].mean(),LR_grid_search.cv_results_['mean_test_AUC'].mean()))

RandomForestClassifier

model = RandomForestClassifier()
params = {"n_estimators":[x for x in range(30,50,2)],'min_samples_split':[x for x in range(4,10)]}
RFC_grid_search = train(model,params)
print("test_Accuracy : {}\ntest_AUC : {}".format(RFC_grid_search.cv_results_['mean_test_Accuracy'].mean(),RFC_grid_search.cv_results_['mean_test_AUC'].mean()))

StackingClassifier

estimators = [
    ('SVC',SVC_grid_search.best_estimator_),
    ('NB', LR_grid_search.best_estimator_),
    ('RFC', RFC_grid_search.best_estimator_)
]
model = StackingClassifier(estimators=estimators, final_estimator=SVC())
model_score = cross_validate(model,feture_data, label,scoring=scores)
print("test_Accuracy : {}\ntest_AUC : {}".format(model_score['test_Accuracy'].mean(),model_score['test_AUC'].mean()))

SVC预测结果:

1.直接删除缺失值以及异常值删除公式上限Q3+1.5(Q3-Q1);下限计算公式为Q1-1.5(Q3-Q1)

SVC(C=1.097979797979798)
test_Accuracy : 0.8549075391180654
test_AUC : 0.511601411290322

在这里插入图片描述
在这里插入图片描述

2.直接删除缺失值以及异常值删除公式上限Q3+3(Q3-Q1);下限计算公式为Q1-3(Q3-Q1)

SVC(C=1.405050505050505)
test_Accuracy : 0.7953321596244133
test_AUC : 0.7133755225726653

在这里插入图片描述
在这里插入图片描述

3.中值填充以及异常值删除公式上限Q3+3(Q3-Q1);下限计算公式为Q1-3(Q3-Q1)

SVC(C=1.7888888888888888)
test_Accuracy : 0.7814101086443079
test_AUC : 0.7248522348166069

在这里插入图片描述
在这里插入图片描述

十三、总结

1.一些删除数据值的处理方法导致样本标签的不均衡会导致对比例大的样本造成过拟合,也就是说预测偏向样本数较多的分类。这样就会大大降低模型的泛化能力。表现在准确率很高,但roc_auc_score很低。上面SVC的预测结果就很好的说明了。

2.可以看出由于缺失值比较多,所以反而各种填充方法的效果比直接删除的效果是要更差的(也有可能我没找到合适的填充方法)

3.关于离群值的处理,主要方法有直接删除法,替换为缺失值处理,以及中值填充法等。由于缺失值处理那里的效果不是很理想,所以就选择了直接删除,并且在平衡了roc_auc_score和accuracy两个指标后,选择只删除极端异常点。

4.关于样本0/1比例的问题,可以考虑上采样或者下采样的方法平衡样本。本文不涉及。


推荐阅读
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • sklearn数据集库中的常用数据集类型介绍
    本文介绍了sklearn数据集库中常用的数据集类型,包括玩具数据集和样本生成器。其中详细介绍了波士顿房价数据集,包含了波士顿506处房屋的13种不同特征以及房屋价格,适用于回归任务。 ... [详细]
  • 不同优化算法的比较分析及实验验证
    本文介绍了神经网络优化中常用的优化方法,包括学习率调整和梯度估计修正,并通过实验验证了不同优化算法的效果。实验结果表明,Adam算法在综合考虑学习率调整和梯度估计修正方面表现较好。该研究对于优化神经网络的训练过程具有指导意义。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文介绍了机器学习手册中关于日期和时区操作的重要性以及其在实际应用中的作用。文章以一个故事为背景,描述了学童们面对老先生的教导时的反应,以及上官如在这个过程中的表现。同时,文章也提到了顾慎为对上官如的恨意以及他们之间的矛盾源于早年的结局。最后,文章强调了日期和时区操作在机器学习中的重要性,并指出了其在实际应用中的作用和意义。 ... [详细]
  • Python爬虫中使用正则表达式的方法和注意事项
    本文介绍了在Python爬虫中使用正则表达式的方法和注意事项。首先解释了爬虫的四个主要步骤,并强调了正则表达式在数据处理中的重要性。然后详细介绍了正则表达式的概念和用法,包括检索、替换和过滤文本的功能。同时提到了re模块是Python内置的用于处理正则表达式的模块,并给出了使用正则表达式时需要注意的特殊字符转义和原始字符串的用法。通过本文的学习,读者可以掌握在Python爬虫中使用正则表达式的技巧和方法。 ... [详细]
  • 背景应用安全领域,各类攻击长久以来都危害着互联网上的应用,在web应用安全风险中,各类注入、跨站等攻击仍然占据着较前的位置。WAF(Web应用防火墙)正是为防御和阻断这类攻击而存在 ... [详细]
  • 手把手教你使用GraphPad Prism和Excel绘制回归分析结果的森林图
    本文介绍了使用GraphPad Prism和Excel绘制回归分析结果的森林图的方法。通过展示森林图,可以更加直观地将回归分析结果可视化。GraphPad Prism是一款专门为医学专业人士设计的绘图软件,同时也兼顾统计分析的功能,操作便捷,可以帮助科研人员轻松绘制出高质量的专业图形。文章以一篇发表在JACC杂志上的研究为例,利用其中的多因素回归分析结果来绘制森林图。通过本文的指导,读者可以学会如何使用GraphPad Prism和Excel绘制回归分析结果的森林图。 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • YOLOv7基于自己的数据集从零构建模型完整训练、推理计算超详细教程
    本文介绍了关于人工智能、神经网络和深度学习的知识点,并提供了YOLOv7基于自己的数据集从零构建模型完整训练、推理计算的详细教程。文章还提到了郑州最低生活保障的话题。对于从事目标检测任务的人来说,YOLO是一个熟悉的模型。文章还提到了yolov4和yolov6的相关内容,以及选择模型的优化思路。 ... [详细]
  • 本文介绍了Python爬虫技术基础篇面向对象高级编程(中)中的多重继承概念。通过继承,子类可以扩展父类的功能。文章以动物类层次的设计为例,讨论了按照不同分类方式设计类层次的复杂性和多重继承的优势。最后给出了哺乳动物和鸟类的设计示例,以及能跑、能飞、宠物类和非宠物类的增加对类数量的影响。 ... [详细]
  • Day2列表、字典、集合操作详解
    本文详细介绍了列表、字典、集合的操作方法,包括定义列表、访问列表元素、字符串操作、字典操作、集合操作、文件操作、字符编码与转码等内容。内容详实,适合初学者参考。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • 本文介绍了200个经典c语言源代码,包括函数的使用,如sqrt函数、clanguagefunct等。这些源代码可以帮助读者更好地理解c语言的编程方法,并提供了实际应用的示例。 ... [详细]
  • Python使用Pillow包生成验证码图片的方法
    本文介绍了使用Python中的Pillow包生成验证码图片的方法。通过随机生成数字和符号,并添加干扰象素,生成一幅验证码图片。需要配置好Python环境,并安装Pillow库。代码实现包括导入Pillow包和随机模块,定义随机生成字母、数字和字体颜色的函数。 ... [详细]
author-avatar
hk129
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有