1.实验目的
掌握KNN的算法原理和具体分类实验方法
2.KNN原理
KNN是通过测量不同特征值之间的距离进行分类。
如果一个样本在特征空间中的k个最邻近的样本中的大多数属于某一个类别,则该样本也划分为这个类别。KNN算法中,所选择的邻居都是已经正确分类的对象。该方法在定类决策上只依据最邻近的一个或者几个样本的类别来决定待分样本所属的类别。
算法的描述:
3.实验数据
实验数据来自Kaggle平台
Heart Disease UCI数据集包含14个属性,分类的target为是否患有心脏病,分别由0,1表示。
属性包括:
- age 年龄
- sex 性别
- chest pain type 胸痛的类型
- resting blood pressure 静息血压
- serum cholestoral in mg/dl 血清胆固醇含量
- fasting blood sugar > 120 mg/dl 空腹血糖
- resting electrocardiographic results (values 0,1,2) 静息心电图结果
- maximum heart rate achieved 最高心跳率
- exercise induced angina 运动诱发的心绞痛
- oldpeak = ST depression induced by exercise relative to rest 运动引起的ST抑制(相对于休息)
- the slope of the peak exercise ST segment 运动ST段的峰值斜率
- number of major vessels (0-3) colored by flourosopy 主要血管数目(0-3)
- thal: 3 = normal; 6 = fixed defect; 7 = reversable defect 地中海贫血
3.1数据分析
1.导入必要的库
# 基本操作
import numpy as np
import pandas as pd
import pandas_profiling
# 数据可视化
import matplotlib.pyplot as plt
import seaborn as sns
# 高级可视化
import plotly.offline as py
from plotly.offline import init_notebook_mode, iplot
import plotly.graph_objs as go
init_notebook_mode(connected = True)
from bubbly.bubbly import bubbleplot
2.观察数据
data = pd.read_csv('heart.csv')
data.head()
age | sex | cp | trestbps | chol | fbs | restecg | thalach | exang | oldpeak | slope | ca | thal | target |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 63 | 1 | 3 | 145 | 233 | 1 | 0 | 150 | 0 | 2.3 | 0 | 0 | 1 |
1 | 37 | 1 | 2 | 130 | 250 | 0 | 1 | 187 | 0 | 3.5 | 0 | 0 | 2 |
2 | 41 | 0 | 1 | 130 | 204 | 0 | 0 | 172 | 0 | 1.4 | 2 | 0 | 2 |
3 | 56 | 1 | 1 | 120 | 236 | 0 | 1 | 178 | 0 | 0.8 | 2 | 0 | 2 |
4 | 57 | 0 | 0 | 120 | 354 | 0 | 1 | 163 | 1 | 0.6 | 2 | 0 | 2 |
共303行14列
data.shape
#(303,14)
3.绘制heat map
任意两个变量的相关系数使用函数data.corr()
得到,上面的heat map显示了给定数据集的不同属性之间的相关性
# making a heat map
plt.rcParams['figure.figsize'] = (20, 15) #设置图像细节,显示图像的最大范围
plt.style.use('ggplot')
sns.heatmap(data.corr(), annot = True, cmap = 'Wistia')
plt.title('Heatmap for the Dataset', fontsize = 20)
plt.show()
从该热力图可以看出,数据集中给出的几乎所有特征/属性之间的相关性都很低。
4.观察患者的年龄分布
# 患者年龄分布
import warnings
warnings.filterwarnings('ignore')
plt.style.use('fivethirtyeight')
plt.rcParams['figure.figsize'] = (15, 5)
sns.distplot(data['age'], color = 'cyan')
plt.title('Distribution of Age', fontsize = 20)
plt.show()
5.绘制出患者的性别分布
size = data['sex'].value_counts()
colors = ['lightblue', 'lightgreen']
labels = "Male", "Female"
explode = [0, 0.01]
my_circle = plt.Circle((0, 0), 0.7, color = 'white') #(0,0)为圆心,0.7为半径
plt.rcParams['figure.figsize'] = (9, 9)
plt.pie(size, colors = colors, labels = labels, shadow = True, explode = explode, autopct = '%.2f%%') #绘制一个饼状图,参数explode为离开圆心的距离,autopct数据标签保留小数点后两位
plt.title('Distribution of Gender', fontsize = 20)
p = plt.gcf() #获取当前图标
p.gca().add_artist(my_circle) #p.gca()获取子图,add_artisi()将画好的饼状图添加进去
plt.legend()
plt.show()
6.绘制出目标类别的分布
样本类别分布比较均匀
# plotting the target attribute
plt.rcParams['figure.figsize'] = (15, 7)
plt.style.use('seaborn-talk')
sns.countplot(data['target'], palette = 'pastel') #使用条形图显示每个分类中的值,参数palette为选择色调
plt.title('Distribution of Target', fontsize = 20)
plt.show()
7.绘制属性静息血压与类别的关系图
图为,患者静息血压与患者是否有心脏病之间的二元图,由图可知不易患病的人的血压略高于易患病的人。
plt.rcParams['figure.figsize'] = (12, 9)
sns.boxplot(data['target'], data['trestbps'], palette = 'viridis') #绘制箱形图以显示类别的分布
plt.title('Relation of tresbps with target', fontsize = 20)
plt.show()
箱型图包括最大值、最小值、中位数、及上下四分位数,上面的点为异常值。四分位数(Quartile)也称四分位点,是指在统计学中把所有数值由小到大排列并分成四等份,处于三个分割点位置的数值。
8.绘制胆固醇与类别的关系图
由图可知,与不患病的患者想比,患病的患者的胆固醇水平更高,我们可以推断胆固醇水平在是否得心脏病中起较大的影响作用。
plt.rcParams['figure.figsize'] = (12, 9)
sns.violinplot(data['target'], data['chol'], palette = 'colorblind') #绘制小提琴图
plt.title('Relation of Cholestrol with Target', fontsize = 20, fontweight = 30)
plt.show()
小提琴图 (Violin Plot) 用于显示数据分布及其概率密度。
这种图表结合了箱形图和密度图的特征,主要用来显示数据的分布形状。中间的黑色粗条表示四分位数范围,从其延伸的幼细黑线代表 95% 置信区间,而白点则为中位数。
9.绘制静息心电图与类别的关系图
由图中柱状图可以发现,在不患病的患者中restecg
值为0的人数显著较高,在患病的患者中restecg
值为1的人数明显较多
plt.rcParams['figure.figsize'] = (12, 9)
dat = pd.crosstab(data['target'], data['restecg']) #crosstab交叉表,可以统计出每类'target'对应着多少'restecg'类
dat.div(dat.sum(1).astype(float), axis = 0).plot(kind = 'bar',
stacked = False,
color = plt.cm.rainbow(np.linspace(0, 1, 4)))
plt.title('Relation of ECG measurement with Target', fontsize = 20, fontweight = 30)
plt.show()
10.绘制血管数与类别关系图
由图我们可以看出,不患病患者血管数整体较低,而患病患者血管数整体较高;即血管数越多,患心脏病的机会就越高
sns.boxenplot(data['target'], data['ca'], palette = 'Reds')
plt.title('Relation between no. of major vessels and target', fontsize = 20, fontweight = 30)
plt.show()
11.绘制年龄与类别关系图
患病与不患病患者的年龄分布没有明显差距,可知,无论年龄大小都要注意心脏的问题。
plt.rcParams['figure.figsize'] = (15, 9)
sns.swarmplot(data['target'], data['age'], palette = 'winter', size = 10)#绘制具有不重叠点的分类散点图。
plt.title('Relation of Age and target', fontsize = 20, fontweight = 30)
plt.show()
12.绘制性别与类别关系图
male = 1, female = 0
由图中可知,在不患病的病人中,男性比女性明显较多;在患病人群中,男性和女性数量几乎相同
sns.boxenplot(data['target'], data['sex'], palette = 'Set3')
plt.title('Relation of Sex and target', fontsize = 20, fontweight = 30)
plt.show()
13.地中海贫血与类别关系图
与不易患心脏病的患者相比,患有心脏病的患者得地中海贫血症的可能性较小
sns.boxenplot(data['target'], data['thal'], palette = 'magma')
plt.title('Relation between Target and Blood disorder-Thalessemia', fontsize = 20, fontweight = 30)
plt.show()
4.实验结果及分析
使用KNN进行分类
随着K值得增加,一开始训练准确率会增高,然后趋于平稳,从K=155开始,准确率一直下降
from sklearn.neighbors import KNeighborsClassifier
k_score = []
k_range = range(1,228)
for k in range(1,228):
knn = KNeighborsClassifier(n_neighbors=k)
knn.fit(X_train_std,y_train)
k_score.append([k,knn.score(X_test_std,y_test)])
k_score = np.array(k_score)
k_score = pd.DataFrame(k_score,columns=['Value of K for KNN','Score'])
fmri = sns.load_dataset("fmri")
ax = sns.lineplot(data=k_score,x="Value of K for KNN", y="Score",)
当k = 7时,准确率最高
Training Accuracy : 0.8546255506607929
Testing Accuracy : 0.868421052631579
precision | recall | f1-score | support | |
---|---|---|---|---|
0 | 0.90 | 0.79 | 0.84 | 33 |
1 | 0.85 | 0.93 | 0.89 | 43 |
accuracy | 0.87 | 76 | ||
macro avg | 0.87 | 0.86 | 0.86 | 76 |
weighted avg | 0.87 | 0.87 | 0.87 | 76 |