糖尿病数据集PimaIndiansdiabetes分类预测

桌桌 2022-9-27 206 9/27

题目信息

实践课要求对糖尿病数据集使用KNN算法进行训练,对测试集进行分类,题目具体如下:

数据描述

皮马印第安人糖尿病数据集由8个医学预测变量和1个目标变量Outcome组成:

  【1】Pregnancies:怀孕次数   【2】Glucose:葡萄糖   【3】BloodPressure:血压 (mm Hg)   【4】SkinThickness:皮层厚度 (mm)   【5】Insulin:胰岛素 2小时血清胰岛素(𝑚𝑢𝑈/𝑚𝑙)(muU/ml)   【6】BMI:体重指数 (体重/身高)2(体重/身高)2   【7】DiabetesPedigreeFunction:糖尿病谱系功能   【8】Age:年龄 (岁)   【9】Outcome:类标变量 (0或1)

  完整的训练集PimaIndiansdiabetes.csv和测试集PimaIndiansdiabetes-test.csv放在当前目录下。

题目要求

  请使用KNN算法对此数据集进行分类,并对测试集PimaIndiansdiabetes-test.csv进行分类,将分类结果保存为csv文件,编码为utf_8_sig。   保存的csv文件格式要求如下:

  • header分别为“ID”和“Outcome”。

  • 第一列为ID,如:“0”,第二列为标签,如:“1”。

解题过程

KNN算法
优点

在处理边界不规则数据的分类问题比线性分类器的效果好,不规则数据很难找到一条线来分割所有样本数据,但是KNN是以样本为中心画一个圈,这就不用考虑数据的边界问题。

使用jupyter,比较好用,代码块的使用相当方便,将代码的功能分割成一个个区块,方便理解和分析解题过程。

缺点

①只适合小数据集,对于大数据集,一般选择KNN的优化算法,比如kd-tree来实现。

②KNN算法对数据容错率低,因为KNN算法对数据样本质量的依赖很高,如果训练数据集中存在错误的样本数据,又离待测样本很近,就会直接导致预测数据不准确。

基本了解数据

导入所需要的包

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt  # 可视化
import seaborn as sns  # matplotlib的高级API
from sklearn.model_selection import train_test_split, cross_val_score
from sklearn.neighbors import KNeighborsClassifier, RadiusNeighborsClassifier

以上导入的包随着一步步使用逐渐导入

数据直观观察和统计性描述
# 先查看训练集的样子
# 导入数据
data = pd.read_csv("./PimaIndiansdiabetes.csv")
X = data.iloc[:, 0:8]
Y = data.iloc[:, -1]
print(type(Y))
# 查看数据类型以及数据数值
print(data)
# print("-------------------------")
# 对数据集进行描述性统计
print(data.describe())
# 查看是否具有数据不均衡的表现
data.groupby('Outcome').size() 

结果如下所示:

<class 'pandas.core.series.Series'>
     Pregnancies  Glucose  BloodPressure  SkinThickness  Insulin   BMI  \
0              6      148             72             35        0  33.6   
1              1       85             66             29        0  26.6   
2              8      183             64              0        0  23.3   
3              1       89             66             23       94  28.1   
4              0      137             40             35      168  43.1   
..           ...      ...            ...            ...      ...   ...   
763           10      101             76             48      180  32.9   
764            2      122             70             27        0  36.8   
765            5      121             72             23      112  26.2   
766            1      126             60              0        0  30.1   
767            1       93             70             31        0  30.4   

     DiabetesPedigreeFunction  Age  Outcome  
0                       0.627   50        1  
1                       0.351   31        0  
2                       0.672   32        1  
3                       0.167   21        0  
4                       2.288   33        1  
..                        ...  ...      ...  
763                     0.171   63        0  
764                     0.340   27        0  
765                     0.245   30        0  
766                     0.349   47        1  
767                     0.315   23        0  

[768 rows x 9 columns]
       Pregnancies     Glucose  BloodPressure  SkinThickness     Insulin  \
count   768.000000  768.000000     768.000000     768.000000  768.000000   
mean      3.845052  120.894531      69.105469      20.536458   79.799479   
std       3.369578   31.972618      19.355807      15.952218  115.244002   
min       0.000000    0.000000       0.000000       0.000000    0.000000   
25%       1.000000   99.000000      62.000000       0.000000    0.000000   
50%       3.000000  117.000000      72.000000      23.000000   30.500000   
75%       6.000000  140.250000      80.000000      32.000000  127.250000   
max      17.000000  199.000000     122.000000      99.000000  846.000000   

              BMI  DiabetesPedigreeFunction         Age     Outcome  
count  768.000000                768.000000  768.000000  768.000000  
mean    31.992578                  0.471876   33.240885    0.348958  
std      7.884160                  0.331329   11.760232    0.476951  
min      0.000000                  0.078000   21.000000    0.000000  
25%     27.300000                  0.243750   24.000000    0.000000  
50%     32.000000                  0.372500   29.000000    0.000000  
75%     36.600000                  0.626250   41.000000    1.000000  
max     67.100000                  2.420000   81.000000    1.000000  
Outcome
0    500
1    268
dtype: int64
数据可视化分析

然后将数据分布进行一个可视化

# 将数据的分布进行可视化
data.hist(figsize=(16, 14));

结果如下图所示

糖尿病数据集PimaIndiansdiabetes分类预测

分析

数据集中有缺失值,但是也查看了测试集中的样本,其中缺失值的比例几乎相同。

主要缺失值在胰岛素水平值上,其中的有一大部分值是0,查阅资料可以知道,人的正常值是5-20。(!!!此处应该对这些异常值进行处理,比如分类均值填补数据集中的0,0的取值即不科学,对分类也会造成一定的影响,想法是对于训练集中的数据,根据是否为糖尿病人进行分类,分类后计算他们的平均值,替代训练集中的不合理的数值0,(是不合理的!!怀孕次数为0是合理的!!),而训练集我打算就使用全体的平均值替代,因为不好确定他们的所属类别)

糖尿病的数量较少,但是没有出现数据不平衡的情况。

可以看到数值之间相差比较大,并且有量纲的影响,需要进行标准化处理。

但是给出的训练集和测试集是在两个文件之中,所以需要合并之后统一进行处理

缺失值处理

这里用全体的平均值替代了不合理的为0的缺失值

再次看一看可视化效果,如下所示

糖尿病数据集PimaIndiansdiabetes分类预测

数据如下所示:

<class 'pandas.core.series.Series'>
     Pregnancies  Glucose  BloodPressure  SkinThickness  Insulin   BMI  \
0              6      148             72             35       80  33.6   
1              1       85             66             29       80  26.6   
2              8      183             64             21       80  23.3   
3              1       89             66             23       94  28.1   
4              0      137             40             35      168  43.1   
..           ...      ...            ...            ...      ...   ...   
763           10      101             76             48      180  32.9   
764            2      122             70             27       80  36.8   
765            5      121             72             23      112  26.2   
766            1      126             60             21       80  30.1   
767            1       93             70             31       80  30.4   

     DiabetesPedigreeFunction  Age  Outcome  
0                       0.627   50        1  
1                       0.351   31        0  
2                       0.672   32        1  
3                       0.167   21        0  
4                       2.288   33        1  
..                        ...  ...      ...  
763                     0.171   63        0  
764                     0.340   27        0  
765                     0.245   30        0  
766                     0.349   47        1  
767                     0.315   23        0  

[768 rows x 9 columns]
       Pregnancies     Glucose  BloodPressure  SkinThickness     Insulin  \
count   768.000000  768.000000     768.000000     768.000000  768.000000   
mean      3.845052  121.682292      72.250000      26.743490  118.757812   
std       3.369578   30.435999      12.117203       9.546733   93.039581   
min       0.000000   44.000000      24.000000       7.000000   14.000000   
25%       1.000000   99.750000      64.000000      21.000000   80.000000   
50%       3.000000  117.000000      72.000000      23.000000   80.000000   
75%       6.000000  140.250000      80.000000      32.000000  127.250000   
max      17.000000  199.000000     122.000000      99.000000  846.000000   

              BMI  DiabetesPedigreeFunction         Age     Outcome  
count  768.000000                768.000000  768.000000  768.000000  
mean    32.450911                  0.471876   33.240885    0.348958  
std      6.875366                  0.331329   11.760232    0.476951  
min     18.200000                  0.078000   21.000000    0.000000  
25%     27.500000                  0.243750   24.000000    0.000000  
50%     32.000000                  0.372500   29.000000    0.000000  
75%     36.600000                  0.626250   41.000000    1.000000  
max     67.100000                  2.420000   81.000000    1.000000  
Outcome
0    500
1    268
dtype: int64
标准化处理
# 导入数据
traindata = pd.read_csv("./PimaIndiansdiabetes.csv")
testdata = pd.read_csv("./PimaIndiansdiabetes-test.csv")

alldata = pd.concat([traindata,testdata])
print(alldata)

结果如下所示:

    Pregnancies  Glucose  BloodPressure  SkinThickness  Insulin   BMI  \
0             6      148             72             35       80  33.6   
1             1       85             66             29       80  26.6   
2             8      183             64             21       80  23.3   
3             1       89             66             23       94  28.1   
4             0      137             40             35      168  43.1   
..          ...      ...            ...            ...      ...   ...   
95           10      101             76             48      180  32.9   
96            2      122             70             27       80  36.8   
97            5      121             72             23      112  26.2   
98            1      126             60             20       80  30.1   
99            1       93             70             31       80  30.4   

    DiabetesPedigreeFunction  Age  Outcome    ID  
0                      0.627   50      1.0   NaN  
1                      0.351   31      0.0   NaN  
2                      0.672   32      1.0   NaN  
3                      0.167   21      0.0   NaN  
4                      2.288   33      1.0   NaN  
..                       ...  ...      ...   ...  
95                     0.171   63      NaN  95.0  
96                     0.340   27      NaN  96.0  
97                     0.245   30      NaN  97.0  
98                     0.349   47      NaN  98.0  
99                     0.315   23      NaN  99.0  

[868 rows x 10 columns]
划分数据集X Y
# 划分训练数据集,统一进行处理
X = alldata.iloc[:, 0:8]
# Y是训练集的分类结果
Y = alldata.iloc[0:768, -2]

print(X)
print(Y)
print(type(Y))

结果如下:

    Pregnancies  Glucose  BloodPressure  SkinThickness  Insulin   BMI  \
0             6      148             72             35       80  33.6   
1             1       85             66             29       80  26.6   
2             8      183             64             21       80  23.3   
3             1       89             66             23       94  28.1   
4             0      137             40             35      168  43.1   
..          ...      ...            ...            ...      ...   ...   
95           10      101             76             48      180  32.9   
96            2      122             70             27       80  36.8   
97            5      121             72             23      112  26.2   
98            1      126             60             20       80  30.1   
99            1       93             70             31       80  30.4   

    DiabetesPedigreeFunction  Age  
0                      0.627   50  
1                      0.351   31  
2                      0.672   32  
3                      0.167   21  
4                      2.288   33  
..                       ...  ...  
95                     0.171   63  
96                     0.340   27  
97                     0.245   30  
98                     0.349   47  
99                     0.315   23  

[868 rows x 8 columns]
0      1.0
1      0.0
2      1.0
3      0.0
4      1.0
      ... 
763    0.0
764    0.0
765    0.0
766    1.0
767    0.0
Name: Outcome, Length: 768, dtype: float64
<class 'pandas.core.series.Series'>
标准化处理
# 数据标准化处理
from sklearn  import  preprocessing
X_all_scaled = preprocessing.scale(X)
print(X_all_scaled)

结果如下:

[[ 0.63202204  0.85835214 -0.03283457 ...  0.14954285  0.49306912
   1.4117557 ]
 [-0.85146939 -1.21892385 -0.52422086 ... -0.87046426 -0.36110387
  -0.19983667]
 [ 1.22541861  2.01239435 -0.68801629 ... -1.35132475  0.63233646
  -0.11501602]
 ...
 [ 0.33532375 -0.031909   -0.03283457 ... -0.92875038 -0.68915582
  -0.28465732]
 [-0.85146939  0.13295417 -1.01560715 ... -0.36046071 -0.36729353
   1.15729374]
 [-0.85146939 -0.95514277 -0.19663    ... -0.31674612 -0.47251774
  -0.87840188]]
划分训练集与待测试数据
# 将待测试的数据集和待测试的测试集分开
X_train_all = X_all_scaled[0:768, 0:8]
print(X_train_all)
X_tobe_test = X_all_scaled[768:868, 0:8]
print("--------------------------------------")
print(X_tobe_test)

结果如下:

[[ 0.63202204  0.85835214 -0.03283457 ...  0.14954285  0.49306912
   1.4117557 ]
 [-0.85146939 -1.21892385 -0.52422086 ... -0.87046426 -0.36110387
  -0.19983667]
 [ 1.22541861  2.01239435 -0.68801629 ... -1.35132475  0.63233646
  -0.11501602]
 ...
 [ 0.33532375 -0.031909   -0.03283457 ... -0.92875038 -0.68915582
  -0.28465732]
 [-0.85146939  0.13295417 -1.01560715 ... -0.36046071 -0.36729353
   1.15729374]
 [-0.85146939 -0.95514277 -0.19663    ... -0.31674612 -0.47251774
  -0.87840188]]
--------------------------------------
[[ 6.32022039e-01 -7.90279601e-01 -1.17940258e+00  6.65850163e-01
   7.68822799e-01  2.07828966e-01 -1.16612327e-01  8.18011139e-01]
 [ 1.52211690e+00  1.05618795e+00  4.58551721e-01  3.47824883e-01
  -2.08135040e-01 -2.43888466e-01 -9.39837028e-01  9.87652441e-01]
 [ 6.32022039e-01  1.41888693e+00 -3.60425427e-01 -7.62088228e-02
   5.30010883e-01  1.49542845e-01  5.05448443e-01  1.32693505e+00]
 ...
  [ 3.35323754e-01 -3.19090014e-02 -3.28345677e-02 -3.94234102e-01
  -7.78739946e-02 -9.28750380e-01 -6.89155822e-01 -2.84657323e-01]
 [-8.51469389e-01  1.32954172e-01 -1.01560715e+00 -7.12259382e-01
  -4.25236782e-01 -3.60460707e-01 -3.67293533e-01  1.15729374e+00]
 [-8.51469389e-01 -9.55142775e-01 -1.96629997e-01  4.53833310e-01
  -4.25236782e-01 -3.16746117e-01 -4.72517743e-01 -8.78401879e-01]]

划分数据集的训练集与测试集(留出法)

# 划分训练集训练的训练集与测试集
X_train, X_test, Y_train, Y_test = train_test_split(X_train_all, Y, test_size=0.1, random_state=1024)
Y_train
124    1.0
665    0.0
538    0.0
40     0.0
261    1.0
      ... 
208    0.0
601    0.0
613    0.0
492    0.0
609    0.0
Name: Outcome, Length: 691, dtype: float64
模型初训练

先是留出法

# 模型训练与评估
# 此处用于与之后的结果进行比对,没有确定参数K
# K-近邻算法中K的取值应该为奇数,为了能够得到当前样本对应的分类
# 以下为每个点权重相等的时候的模型
model1 = KNeighborsClassifier(n_neighbors=5)
model1.fit(X_train, Y_train)
score1 = model1.score(X_test, Y_test)

# 我们认为,两个人身体特征相同或者相近的时候,
# 这两个人应该具有较为相似的疾病表现
# 我们设置weight='distance'
model2 = KNeighborsClassifier(n_neighbors=5, weights='distance')
model2.fit(X_train, Y_train)
score2 = model2.score(X_test, Y_test)

交叉验证法并比较结果

# 使用交叉验证
res1 = cross_val_score(model1, X_train_all, Y, cv=10)
res2 = cross_val_score(model2, X_train_all, Y, cv=10)

# 打印结果
print(score1, score2)
print(res1.mean(), res2.mean())

结果为:

0.7402597402597403 0.7532467532467533
0.7395933014354068 0.7369958988380042
确定K的取值
# 确定K的值
score = []
alphas = []
for alpha in range(1,50,1):
    alphas.append(alpha)
    rdg = KNeighborsClassifier(alpha)
    sc = np.sqrt( -cross_val_score(rdg,X_train_all,Y,scoring = "neg_mean_squared_error", cv = 10))
    score.append(sc.mean())
plt.plot(alphas,score)
plt.show()

结果如图所示

image-20220927122942255

由上图可以看出,K的值为29时,损失是最小的,因此选K=29

训练模型
# 模型训练与评估
# K-近邻算法中K的取值应该为奇数,为了能够得到当前样本对应的分类
# 以下为每个点权重相等的时候的模型
model1 = KNeighborsClassifier(n_neighbors=29)
model1.fit(X_train, Y_train)
score1 = model1.score(X_test, Y_test)

# 我们认为,两个人身体特征相同或者相近的时候,
# 这两个人应该具有较为相似的疾病表现
# 我们设置weight='distance'
model2 = KNeighborsClassifier(n_neighbors=29, weights='distance')
model2.fit(X_train, Y_train)
score2 = model2.score(X_test, Y_test)

# 使用交叉验证
res1 = cross_val_score(model1, X_train_all, Y, cv=10)
res2 = cross_val_score(model2, X_train_all, Y, cv=10)

# 打印结果
print(score1, score2)
print(res1.mean(), res2.mean())

结果如下:

0.8311688311688312 0.8181818181818182
0.7773581681476418 0.7708475734791524
绘制学习曲线
#绘制学习曲线
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit
def plot_learning_curve(estimator, title, X, y, ylim=None, cv=None,
                        n_jobs=1, train_sizes=np.linspace(.1, 1.0, 5)):
    plt.figure()
    plt.title(title)
    if ylim is not None:
        plt.ylim(*ylim)
    plt.xlabel("Training examples")
    plt.ylabel("Score")
    train_sizes, train_scores, test_scores = learning_curve(
        estimator, X, y, cv=cv, n_jobs=n_jobs, train_sizes=train_sizes)
    train_scores_mean = np.mean(train_scores, axis=1)
    train_scores_std = np.std(train_scores, axis=1)
    test_scores_mean = np.mean(test_scores, axis=1)
    test_scores_std = np.std(test_scores, axis=1)
    plt.grid()
 
    plt.fill_between(train_sizes, train_scores_mean - train_scores_std,
                     train_scores_mean + train_scores_std, alpha=0.1,
                     color="r")
    plt.fill_between(train_sizes, test_scores_mean - test_scores_std,
                     test_scores_mean + test_scores_std, alpha=0.1, color="g")
    plt.plot(train_sizes, train_scores_mean, 'o-', color="r",
             label="Training score")
    plt.plot(train_sizes, test_scores_mean, 'o-', color="g",
             label="Cross-validation score")
 
    plt.legend(loc="best")
    return plt



knn = KNeighborsClassifier(n_neighbors=21)
cv = ShuffleSplit(n_splits=10, test_size=0.1, random_state=1024)
plt.figure(figsize=(10, 6))
plot_learning_curve( knn, "Learn Curve for KNN Diabetes", 
                    X_train_all, Y, (0.5,1.01), cv=cv);
plt.show()

结果图如下:

糖尿病数据集PimaIndiansdiabetes分类预测

导入待预测的数据进行预测

先前处理好了数据,在这里直接用X_tobe_test

res=pd.DataFrame(model1.predict(X_tobe_test))
outcome=pd.concat([test_id,res],axis=1)
outcome.to_csv('./outcome.csv',header=['ID','Outcome'],index=False,encoding='utf_8_sig')
尝试PCA降维

考虑特征是否有多余的,由此使用person相关分析如下:

# 导入数据
data = pd.read_csv("./PimaIndiansdiabetes.csv")
X = data.iloc[:, 0:8]
Y = data.iloc[:, -1]
# 先看一下person相关系数矩阵(这里画成了热力图,方便比较)
corr = data.corr()  # 计算变量的相关系数,得到一个N * N的矩阵

plt.subplots(figsize=(14,12)) # 可以先试用plt设置画布的大小,然后在作图,修改
sns.heatmap(corr, annot = True) # 使用热度图可视化这个相关系数矩阵
plt.show()

结果如图所示:

糖尿病数据集PimaIndiansdiabetes分类预测

可以看出,虽然有一些特征与Outcome的相关系数并不大,尝试降维。

划分数据集以及标准化处理与以上方法一致

代码如下:

# 进行PCA降维
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt  # 可视化
from sklearn.decomposition import PCA

pca = PCA(n_components=0.90)# 保证降维后的数据保持90%的信息
pca.fit(X_all_scaled)
X_all_scaled = pca.transform(X_all_scaled)
X_all_scaled

结果如下:

[[ 1.40387844 -0.71387504  0.02562345 ... -0.44985738 -0.87652201
   0.72606078]
 [-1.50773494  0.25009407 -0.46039451 ... -0.3366215   0.36535497
   0.92392879]
 [ 0.07073991 -1.00461334  1.84145542 ...  0.06379412 -1.4691181
  -0.40918533]
 ...
 [-0.74972145 -0.64302622  0.31654953 ... -0.08207593  0.12649778
  -0.03331613]
 [-0.75295036 -0.45294644  0.53962514 ... -0.12035791 -0.81639477
   0.88124598]
 [-1.20872818  0.76484397 -0.83339878 ... -0.09029731  0.25933703
   0.43746935]]
--------------------------------------
[[ 2.34879714e-01 -1.94684846e-02  1.65382999e-01  2.36213843e-01
  -1.85280470e+00  4.02796837e-01  2.31006438e-01]
 [ 1.17605825e+00 -1.66362332e+00  1.05546164e-01 -7.63710104e-01
  -5.28956666e-01 -5.76403919e-01  3.71885086e-02]
 [ 1.40627477e+00 -6.21163929e-01  1.27042072e+00  2.56720733e-01
  -2.79723364e-01 -7.47499464e-01  1.24579210e-01]
 ...
  [-7.94550283e-01 -4.90206809e-01  5.77582712e-01 -7.51469579e-02
  -7.50135533e-02 -8.12100720e-01  8.28709007e-01]
 [-1.20872818e+00  7.64843966e-01 -8.33398782e-01 -2.57749620e-01
  -9.02973092e-02  2.59337032e-01  4.37469354e-01]]

确定k值如图所示:

糖尿病数据集PimaIndiansdiabetes分类预测

于是取K=39

然后训练模型

# 可以看出,在K值取到39时具有较好的效果,这里我们取K=39
# 模型训练与评估
# K-近邻算法中K的取值应该为奇数,为了能够得到当前样本对应的分类
# 以下为每个点权重相等的时候的模型
model1 = KNeighborsClassifier(n_neighbors=39)
model1.fit(X_train, Y_train)
score1 = model1.score(X_test, Y_test)

# 我们认为,两个人身体特征相同或者相近的时候,
# 这两个人应该具有较为相似的疾病表现
# 我们设置weight='distance'
model2 = KNeighborsClassifier(n_neighbors=39, weights='distance')
model2.fit(X_train, Y_train)
score2 = model2.score(X_test, Y_test)

# 使用交叉验证
res1 = cross_val_score(model1, X_train_all, Y, cv=10)
res2 = cross_val_score(model2, X_train_all, Y, cv=10)

# 打印结果
print(score1, score2)
print(res1.mean(), res2.mean())

结果为:

0.7532467532467533 0.7922077922077922
0.7799726589200273 0.7786397812713604

可以看到,效果比没有使用PCA降维效果好一点点。

待预测处理与此前一致。

- THE END -

桌桌

9月27日15:33

最后修改:2022年9月27日
0

非特殊说明,本博所有文章均为博主原创。