KNN分类算法的Python实现


前言

K最近邻(k-Nearest Neighbor,KNN)分类算法,是一个理论上比较成熟的方法,也是最简单的机器学习算法之一。该方法的思路是:在特征空间中,如果一个样本附近的k个最近(即特征空间中最邻近)样本的大多数属于某一个类别,则该样本也属于这个类别。。


一、KNN的基本思路

所谓K近邻算法,即是给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例, 这K个实例的多数属于某个类,就把该输入实例分类到这个类中。并不是很复杂,十分易于理解。主要的算法思路也就分为3步。

  1. 从测试集中选择一个样本,分别计算该样本和训练集中各样本的距离(本文选择欧氏距离)。
  2. 对选取的测试集样本与各训练集样本的距离记录排序,找到距离最小的k个距离。以距离平方的倒数为权重,对这k个样本的标签进行加权投票,得到该测试集样本的标签。
  3. 循环对测试集样本进行上述两步操作。

二、代码实现

程序部分主要有5个部分:

read_xslx(xslx_path)   #读取excel文件
standardization(v,mu,sigma)  #对一个数据规范化
standardize(data)  #对数据集规范化
calDistance(testingrow, trainingrow)  #计算样本间距离
KNN(trainingData, testingData, k)  #KNN算法部分

1.读取excel文件

#读取excel文件,400个为训练集,200个为测试集
####################################################################################################################
def read_xslx(xslx_path):

    dataSet = []                      # 先声明一个空list
    data = xlrd.open_workbook(xslx_path)   # 读取文件
    table = data.sheet_by_index(0)         # 按索引获取工作表,0就是工作表1

    for i in range(table.nrows):  # table.nrows表示总行数
        line = table.row_values(i)         # 读取每行数据,保存在line里面,line是list
        dataSet.append(line)          # 将line加入到trainingdata中,trainingdata是二维list
    dataSet = np.array(dataSet)  # 将trainingdata从二维list变成数组

    return dataSet
####################################################################################################################

2.对一个数据规范化

#规范化处理函数
#####################################################################################
def standardization(v,mu,sigma):
    v = (v - mu)/sigma
    return v
#####################################################################################

3.对数据集规范化

#规范化数据
####################################################################################################################
def standardize(data):

    numTotal = data.shape[0]  # 记录测试数据集总条数
    attr1 = data[:, 0].astype(float)              #标准化过程
    attr2 = data[:, 1].astype(float)
    attr3 = data[:, 2].astype(float)
    attr4 = data[:, 3].astype(float)
    attr5 = data[:, 4].astype(float)
    attr6 = data[:, 5].astype(float)
    Data = data

    mu1 = np.mean(attr1)
    sigma1 = np.std(attr1)
    for i in range(0,numTotal):
        Data[:, 0][i] =  standardization(attr1[i],mu1,sigma1)

    mu2 = np.mean(attr2)
    sigma2 = np.std(attr2)
    for i in range(0,numTotal):
        Data[:, 1][i] =  standardization(attr2[i],mu2,sigma2)

    mu3 = np.mean(attr3)
    sigma3 = np.std(attr3)
    for i in range(0,numTotal):
        Data[:, 2][i] =  standardization(attr3[i],mu3,sigma3)

    mu4 = np.mean(attr4)
    sigma4 = np.std(attr4)
    for i in range(0,numTotal):
        Data[:, 3][i] =  standardization(attr4[i],mu4,sigma4)

    mu5 = np.mean(attr5)
    sigma5 = np.std(attr5)
    for i in range(0,numTotal):
        Data[:, 4][i] =  standardization(attr5[i],mu5,sigma5)

    mu6 = np.mean(attr6)
    sigma6 = np.std(attr6)
    for i in range(0,numTotal):
        Data[:, 5][i] =  standardization(attr6[i],mu6,sigma6)

    return Data
####################################################################################################################

4.计算样本间距离

#计算距离
####################################################################################################################
def calDistance(testingrow, trainingrow):
    dist = ((float(testingrow[0]) - float(trainingrow[0])) ** 2 + (float(testingrow[1]) - float(trainingrow[1])) ** 2 \
            + (float(testingrow[2]) - float(trainingrow[2])) ** 2 + (float(testingrow[3]) - float(trainingrow[3])) ** 2\
            + (float(testingrow[4]) - float(trainingrow[4])) ** 2 + (float(testingrow[5]) - float(trainingrow[5])) ** 2) ** 0.5
    return dist
####################################################################################################################

5.KNN算法

#K邻近算法
####################################################################################################################
def KNN(trainingData, testingData, k):

    numTest = testingData.shape[0]     # 记录测试数据集总条数
    numTrain = trainingData.shape[0]   # 记录训练数据集总条数
    labelTest = []                     # 记录各测试样本的标签分类结果

    for i in range(0, numTest):
        distance = []  # 记录某测试样本和各训练样本的距离
        labelTrain = []  # 记录某测试样本下各训练样本的标签
        labeljudge = [0.0, 0.0, 0.0]
        for j in range(0, numTrain):
            distance.append(calDistance(testingData[i], trainingData[j]))   # 记录某测试样本和各训练样本的距离
            labelTrain.append(trainingData[j][-1])           # 记录各训练样本的标签
        distance2 = distance[:]
        distance2.sort()
        for p in range(0, k):
            a = distance2[p]
            b = distance.index(a)
            temp = labelTrain[b]   # 记录各测试样本的标签分类结果
            if temp == '0.0':
                labeljudge[0] += 1/(distance2[p])**2
            elif temp == '1.0':
                labeljudge[1] += 1/(distance2[p])**2
            elif temp == '2.0':
                labeljudge[2] += 1/(distance2[p])**2
        labelType = labeljudge.index(max(labeljudge))
        if labelType == 0:
            labelTest.append('0.0')
        elif labelType == 1:
            labelTest.append('1.0')
        elif labelType == 2:
            labelTest.append('2.0')

    cm = confusion_matrix(testingData[:,-1], labelTest, labels=["0.0", "1.0", "2.0"])
    plt.rc('figure', figsize=(5, 5))
    plt.matshow(cm, cmap=plt.cm.cool)  # 背景颜色
    plt.colorbar()  # 颜色标签
    # 内部添加图例标签
    for x in range(len(cm)):
        for y in range(len(cm)):
            plt.annotate(cm[x, y], xy=(y, x), horizontalalignment='center', verticalalignment='center')
    plt.ylabel('True Label')
    plt.xlabel('Predicted Label')
    plt.title('KNN')
    plt.savefig(r'confusion_matrix')

    return cm
####################################################################################################################

完整代码

# -*- coding: utf-8 -*-     支持文件中出现中文字符
#########################################################################

"""
Created on Thur Nov 19 16:32:00 2020

@author: ixobgnew

代码功能描述:(1)读取xlsx文件
            (2)分离数据集
            (3)数据规范化处理
            (4)计算数据欧氏距离
            (5)K邻近算法
            (6)生成混淆矩阵

"""
#####################################################################

import xlrd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import confusion_matrix

#读取excel文件,400个为训练集,200个为测试集
####################################################################################################################
def read_xslx(xslx_path):

    dataSet = []                      # 先声明一个空list
    data = xlrd.open_workbook(xslx_path)   # 读取文件
    table = data.sheet_by_index(0)         # 按索引获取工作表,0就是工作表1

    for i in range(table.nrows):  # table.nrows表示总行数
        line = table.row_values(i)         # 读取每行数据,保存在line里面,line是list
        dataSet.append(line)          # 将line加入到trainingdata中,trainingdata是二维list
    dataSet = np.array(dataSet)  # 将trainingdata从二维list变成数组

    return dataSet
####################################################################################################################

#规范化处理函数
#####################################################################################
def standardization(v,mu,sigma):
    v = (v - mu)/sigma
    return v
#####################################################################################

#规范化数据
####################################################################################################################
def standardize(data):

    numTotal = data.shape[0]  # 记录测试数据集总条数
    attr1 = data[:, 0].astype(float)              #标准化过程
    attr2 = data[:, 1].astype(float)
    attr3 = data[:, 2].astype(float)
    attr4 = data[:, 3].astype(float)
    attr5 = data[:, 4].astype(float)
    attr6 = data[:, 5].astype(float)
    Data = data

    mu1 = np.mean(attr1)
    sigma1 = np.std(attr1)
    for i in range(0,numTotal):
        Data[:, 0][i] =  standardization(attr1[i],mu1,sigma1)

    mu2 = np.mean(attr2)
    sigma2 = np.std(attr2)
    for i in range(0,numTotal):
        Data[:, 1][i] =  standardization(attr2[i],mu2,sigma2)

    mu3 = np.mean(attr3)
    sigma3 = np.std(attr3)
    for i in range(0,numTotal):
        Data[:, 2][i] =  standardization(attr3[i],mu3,sigma3)

    mu4 = np.mean(attr4)
    sigma4 = np.std(attr4)
    for i in range(0,numTotal):
        Data[:, 3][i] =  standardization(attr4[i],mu4,sigma4)

    mu5 = np.mean(attr5)
    sigma5 = np.std(attr5)
    for i in range(0,numTotal):
        Data[:, 4][i] =  standardization(attr5[i],mu5,sigma5)

    mu6 = np.mean(attr6)
    sigma6 = np.std(attr6)
    for i in range(0,numTotal):
        Data[:, 5][i] =  standardization(attr6[i],mu6,sigma6)

    return Data
####################################################################################################################

#计算距离
####################################################################################################################
def calDistance(testingrow, trainingrow):
    dist = ((float(testingrow[0]) - float(trainingrow[0])) ** 2 + (float(testingrow[1]) - float(trainingrow[1])) ** 2 \
            + (float(testingrow[2]) - float(trainingrow[2])) ** 2 + (float(testingrow[3]) - float(trainingrow[3])) ** 2\
            + (float(testingrow[4]) - float(trainingrow[4])) ** 2 + (float(testingrow[5]) - float(trainingrow[5])) ** 2) ** 0.5
    return dist
####################################################################################################################

#K邻近算法
####################################################################################################################
def KNN(trainingData, testingData, k):

    numTest = testingData.shape[0]     # 记录测试数据集总条数
    numTrain = trainingData.shape[0]   # 记录训练数据集总条数
    labelTest = []                     # 记录各测试样本的标签分类结果

    for i in range(0, numTest):
        distance = []  # 记录某测试样本和各训练样本的距离
        labelTrain = []  # 记录某测试样本下各训练样本的标签
        labeljudge = [0.0, 0.0, 0.0]
        for j in range(0, numTrain):
            distance.append(calDistance(testingData[i], trainingData[j]))   # 记录某测试样本和各训练样本的距离
            labelTrain.append(trainingData[j][-1])           # 记录各训练样本的标签
        distance2 = distance[:]
        distance2.sort()
        for p in range(0, k):
            a = distance2[p]
            b = distance.index(a)
            temp = labelTrain[b]   # 记录各测试样本的标签分类结果
            if temp == '0.0':
                labeljudge[0] += 1/(distance2[p])**2
            elif temp == '1.0':
                labeljudge[1] += 1/(distance2[p])**2
            elif temp == '2.0':
                labeljudge[2] += 1/(distance2[p])**2
        labelType = labeljudge.index(max(labeljudge))
        if labelType == 0:
            labelTest.append('0.0')
        elif labelType == 1:
            labelTest.append('1.0')
        elif labelType == 2:
            labelTest.append('2.0')

    cm = confusion_matrix(testingData[:,-1], labelTest, labels=["0.0", "1.0", "2.0"])
    plt.rc('figure', figsize=(5, 5))
    plt.matshow(cm, cmap=plt.cm.cool)  # 背景颜色
    plt.colorbar()  # 颜色标签
    # 内部添加图例标签
    for x in range(len(cm)):
        for y in range(len(cm)):
            plt.annotate(cm[x, y], xy=(y, x), horizontalalignment='center', verticalalignment='center')
    plt.ylabel('True Label')
    plt.xlabel('Predicted Label')
    plt.title('KNN')
    plt.savefig(r'confusion_matrix')

    return cm
####################################################################################################################



Data113 = read_xslx(r'e:/Table/机器学习/1118/attribute_113.xlsx')
Data114 = read_xslx(r'e:/Table/机器学习/1118/attribute_114.xlsx')
data = np.vstack((Data113[1:,1:],Data114[1:,1:]))
Data = standardize(data)
trainingData = Data[0:397]
testingData = Data[398:600]
ConfusionMatrix = KNN(trainingData, testingData, 4)
print(ConfusionMatrix)

分类结果

需要注意,KNN算法中K的选择对算法的分类效果有着明显影响。
选择K=4时,生成的混淆矩阵:
[[46 0 17]
[ 1 30 2]
[22 1 81]]

可视化结果如下:
在这里插入图片描述
如果改变K的取值,混淆矩阵会明显改变,如下所示:

①k=3:在这里插入图片描述
②k=5:
在这里插入图片描述 ③k=6:

在这里插入图片描述
④k=8:
在这里插入图片描述
⑤k=9:
在这里插入图片描述

总结

本文利用KNN算法所得分类结果并不是很佳,无法很准确地分辨“0”类和“2”类,如果选择其他的度量方式来获取样本间距离,或是采用其他对数据的标准化方法可能可以有所改善。但KNN算法本身是一个比较简单粗暴的方法,还是能起到初步的分类效果的。

  • 8
    点赞
  • 30
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值