机器学习-k近邻算法(kNN算法)的进一步实现

此篇博客不再对k近邻算法加以解释,还不了解k近邻算法的读者可以参考一下我的上一篇博客,有通过一个相对简单的关于集美大学建筑物的例子来讲k-近邻算法。机器学习-k近邻算法及简单实现https://blog.csdn.net/m0_52053228/article/details/127527163?spm=1001.2014.3001.5502

上篇博客的基础上,我本打算扩大集美大学建筑物这个数据集的范围和属性,但数据量还是太小,干脆换一个新的数据集拿来实现k-近邻算法。作为一名预备党员,应该时刻关心人民,想了又想,我就拿我们学院2022秋季入学是否申请生源地贷款来做数据集(完全没有歧视任何一位同学的意思!!!)。


目录

数据集读取

数据集处理

三维散点图

kNN算法

主函数

整体代码

变换k值

总结


数据集读取

我们将住宿费月平均花费家庭平均收入(\月)作为数据集的特征,是否申请生源地贷款作为数据集的标签(为了方便算法,我们将0视为未申请生源地贷款,将1视为已申请生源地贷款)下图给出13条数据,实际数据共计77条。

 

我们定义一个读取数据的函数

将excel数据用二维数组保存

#读取数据集
def read_excel(path):
    raw_data = pd.read_excel(path,header=0)
    #筛除序号一列,所以是读取后面四列
    data = raw_data.values[:,1:5]
    return data

数据集处理

对读取到的数据集进行处理,为了达到实验的效果,我们从数据集中随机抽取10%作为训练集,其余的作为测试集,分别返回训练集、测试集的特征和标签

#从数据集中随机抽取10%作为测试集,其余作为训练集
def classify(data):
    data_test = np.zeros((1,4),dtype=int)
    for i in range(int(data.shape[0]*0.1)):
        index = np.random.randint(0,data.shape[0])
        data = np.delete(data,index,axis=0)
        data_test = np.row_stack((data_test,data[index,:]))
        #print(index)
    #print(data)
    #print(data_test)
    #print(data[:,:3].shape,data[:,-1].shape,data_test[1:,:3],data_test[1:,-1])
    return data[:,:3],data[:,-1],data_test[1:,:3],data_test[1:,-1]

三维散点图

为了更直观地查看数据

我们将住宿费作x轴月平均花费做y轴家庭平均收入做z轴

通过matplotlib将数据集用三维散点图表示出来

#画出三维散点图
def plot(data):
    a0 = np.zeros((1,4),dtype=int)
    a1 = np.ones((1,4),dtype=int)
    #遍历将整个数据集分成两个数据集,来区别是否申请生源地贷款
    for i in range(data.shape[0]):
        if(data[i,3]==0):
            a0 = np.row_stack((a0,data[i,:]))
        else:
            a1 = np.row_stack((a1,data[i,:]))
    #data0为没有申请生源地贷款的学生信息
    data0 = a0[1:,:]
    x1 = data0[:,0]
    y1 = data0[:,1]
    z1 = data0[:,2]
    #print(data0)
    #data1为申请生源地贷款的学生信息
    data1 = a1[1:,:]
    x2 = data1[:,0]
    y2 = data1[:,1]
    z2 = data1[:,2]
    #print(data1)
    fig = plt.figure()
    ax = Axes3D(fig,auto_add_to_figure=False)
    ax.scatter(x1,y1,z1,c='r',label='未申请生源地贷款')
    ax.scatter(x2,y2,z2,c='b',label='已申请生源地贷款')
    ax.legend(loc='best')

    ax.set_zlabel('家庭月平均收入', fontdict={'size': 15, 'color': 'black'})
    ax.set_ylabel('月平均花费', fontdict={'size': 15, 'color': 'black'})
    ax.set_xlabel('住宿费', fontdict={'size': 15, 'color': 'black'})

    fig.add_axes(ax)
    plt.show()

申请生源地贷款的点标红

申请生源地贷款的点标蓝

效果如下

 

kNN算法

kNN算法与前一篇博客的算法一样,就不再做过多的解释

#kNN算法
def kNN(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    sortedDistIndices = distances.argsort()
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndices[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

主函数

主函数通过循环,对测试集的每一个行向量进行kNN算法的运算,并与该测试数据的真实标签进行对比。为了能够区分取不同k值的测试效果,我们定义一个错误率,测试每次实验的错误率(kNN算法返回的标签与其真实的标签不同即为错误)

if __name__=='__main__':
    errorCount = 0.0
    train_features,train_labels,test_features,test_labels=classify(read_excel(data_path))
    for i in range(test_features.shape[0]):
        kNNResult = kNN(test_features[i,:],train_features,train_labels,3)
        print(f'kNN算法返回的测试结果为 {kNNResult},其真实标签为 {test_labels[i]}')
        if(kNNResult!=test_labels[i]):
            errorCount+=1.0
    #plot(read_excel(data_path))
    print(f'此次测试的错误率为 {errorCount/float(test_features.shape[0])}\n\n')

整体代码

from cProfile import label
import numpy as np
import operator
import pandas as pd
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from pylab import mpl
# 设置显示中文字体
mpl.rcParams["font.sans-serif"] = ["SimHei"]
# 设置正常显示符号
mpl.rcParams["axes.unicode_minus"] = False

data_path = r'data.xlsx'

#kNN算法
def kNN(inX, dataSet, labels, k):
    dataSetSize = dataSet.shape[0]
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    sqDiffMat = diffMat ** 2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances ** 0.5
    sortedDistIndices = distances.argsort()
    classCount = {}
    for i in range(k):
        voteIlabel = labels[sortedDistIndices[i]]
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
    return sortedClassCount[0][0]

#读取数据集
def read_excel(path):
    raw_data = pd.read_excel(path,header=0)
    data = raw_data.values[:,1:5]
    return data

#从数据集中随机抽取10%作为测试集,其余作为训练集
def classify(data):
    data_test = np.zeros((1,4),dtype=int)
    for i in range(int(data.shape[0]*0.1)):
        index = np.random.randint(0,data.shape[0])
        data = np.delete(data,index,axis=0)
        data_test = np.row_stack((data_test,data[index,:]))
        #print(index)
    #print(data)
    #print(data_test)
    #print(data[:,:3].shape,data[:,-1].shape,data_test[1:,:3],data_test[1:,-1])
    return data[:,:3],data[:,-1],data_test[1:,:3],data_test[1:,-1]
    
#画出三维散点图
def plot(data):
    a0 = np.zeros((1,4),dtype=int)
    a1 = np.ones((1,4),dtype=int)
    for i in range(data.shape[0]):
        if(data[i,3]==0):
            a0 = np.row_stack((a0,data[i,:]))
        else:
            a1 = np.row_stack((a1,data[i,:]))
    #data0为没有申请生源地贷款的学生信息
    data0 = a0[1:,:]
    x1 = data0[:,0]
    y1 = data0[:,1]
    z1 = data0[:,2]
    #print(data0)
    #data1为申请生源地贷款的学生信息
    data1 = a1[1:,:]
    x2 = data1[:,0]
    y2 = data1[:,1]
    z2 = data1[:,2]
    #print(data1)
    fig = plt.figure()
    ax = Axes3D(fig,auto_add_to_figure=False)
    ax.scatter(x1,y1,z1,c='r',label='未申请生源地贷款')
    ax.scatter(x2,y2,z2,c='b',label='已申请生源地贷款')
    ax.legend(loc='best')

    ax.set_zlabel('家庭月平均收入', fontdict={'size': 15, 'color': 'black'})
    ax.set_ylabel('月平均花费', fontdict={'size': 15, 'color': 'black'})
    ax.set_xlabel('住宿费', fontdict={'size': 15, 'color': 'black'})

    fig.add_axes(ax)
    plt.show()


if __name__=='__main__':
    errorCount = 0.0
    train_features,train_labels,test_features,test_labels=classify(read_excel(data_path))
    for i in range(test_features.shape[0]):
        kNNResult = kNN(test_features[i,:],train_features,train_labels,3)
        print(f'kNN算法返回的测试结果为 {kNNResult},其真实标签为 {test_labels[i]}')
        if(kNNResult!=test_labels[i]):
            errorCount+=1.0
    #plot(read_excel(data_path))
    print(f'此次测试的错误率为 {errorCount/float(test_features.shape[0])}\n\n')

运行结果为

变换k值

想要改变k值来观察错误率的变化,首先我们要明白k值的含义。

给定一个训练集,找到其映射到特征空间中的对应向量,保存下来。当新来了一个测试实例,找到其在特征空间中最近的k个实例,看在这k个实例中属于哪一个类别的最多,就给测试样本赋予对应的标签。

上图结果是在k=3的前提下,错误率为0.57。

下面我们对k值进行修改,观察一下不同k值下错误率的变化。

为了避免k个实例中两种类别的数量相同,我们不讨论k为偶数的情况。

修改k = 5,错误率为0.14

修改k = 1,错误率为0.0(可能是我数据集的问题,类别太好区分,大佬勿喷)

修改k = 7,错误率为0.57

总结

由此可见,k值的选择是k近邻算法的要素之一,因为k取不同的值,测试结果也不同。

不能说k值越小越好,当然也不是越大越好,k值太小,模型容易过拟合,k值太大错误率也会升高。

因此,选择合适的k值对我们的模型会有很大的帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

库里不会写代码

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值