原生python实现knn分类算法(鸢尾花数据集)

一:作业题目

原生python实现knn分类算法(鸢尾花数据集)

二:算法设计

Knn算法的核心思想:一个样本x与样本集中的k个最相邻的样本中的大多数属于某一个类别yLabel,那么该样本x也属于类别yLabel,并具有这个类别样本的特性。简而言之,一个样本与数据集中的k个最相邻样本中的大多数的类别相同。由其思想可以看出,KNN是通过测量不同特征值之间的距离进行分类,而且在决策样本类别时,只参考样本周围k个“邻居”样本的所属类别。因此比较适合处理样本集存在较多重叠的场景,主要用于聚类分析、预测分析、文本分类、降维等,也常被认为是简单数据挖掘算法的分类技术之一。

在确定训练集是,就要确定训练数据及其对应的类别标签;然后把待分类的测试数据与训练集数据依次进行特征比较;从训练集中挑选出最相近的k个数据,这k个数据中投票最多的分类,即为新样本的类别。

空间距离采用“欧式距离”进行计算,表达式如下

原生python实现knn分类算法(鸢尾花数据集)

Knn算法流程图:

原生python实现knn分类算法(鸢尾花数据集)

Knn算法的优点

(1)理论成熟简单,易于理解及算法实现;

(2) 可以用于多分类分类、回归等;

缺点

(1)需要计算待分类样本与所有已知样本的距离,计算量大;

(2)样本容量小或样本分布不均衡时,容易分类错误,后者可通过施加距离权重进行改善

算法整体流程图如下:

 

原生python实现knn分类算法(鸢尾花数据集)

三:源代码

import numpy as np
import pandas as pd
class KNN:
    """使用python实现K近邻算法"""
    def __init__(self,k):
        """初始化方法
        
        Parameters:
        ----
        k:int
            邻居的个数
        """
        self.k = k
    
    def fit(self, X, y):
        """训练方法
        Parameters
        ----
            X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]
            y:类似数组类型,形状为[样本数量]
                每个样本的目标值,也是就是标签
        """
        #将X转换成ndarray类型,如果X已经是ndarray则不进行转换
        self.X = np.asarray(X)
        self.y = np.asarray(y)
    
    def predict(self, X):
        """根据参数传递的样本,对样本数据进行预测,返回预测之后的结果
        Parameters
        ----
        X:类似数组类型,list,ndarray……形状:[样本的数量,特征的数量]
        
        Return
        ----
        result:数类型,预测的结果。
        """
        print(data)
        #将测试的X转换为ndarray结构
        X = np.asarray(X)
        result = []
        
        for x in X:
            #ndarray相减为对应元素相减,测试的X的每一行与self.X 相减
            #求欧氏距离:每个元素都取平方值
            dis = np.sqrt(np.sum((x - self.X) ** 2,axis = 1))
            #求最近的k个点的距离,sort()排序不适用,因为排序后打乱了顺序
            #argsort(),返回每个元素在排序之前原数组的索引
            index = dis.argsort()
            #取前k个元素,距离最近的k的元素
            index = index[:self.k]
            #返回数组中每个元素出现的次数,元素必须是非负整数
            count = np.bincount(self.y[index])
            #返回ndarray之最大的元素的索引,该索引就是我们判定的类别
            result.append(count.argmax())
        return np.asarray(result)
    
    def score(self,r,Y):
        return r/Y.size
#读取数据集,header参数来指定参数标题的行,默认为0,第一行,如果没有标题使用None
data = pd.read_csv('iris.csv',header=0)
#对文本进行处理,将Species列的文本映射成数值类型
data['Species'] = data['Species'].map({'virginica':0,'setosa':1,'versicolor':2})
#data.head(20)
#显示末尾行数
# data.tail(20)
#随机显示,默认为1条
data.sample(10)
#删除不需要的列
data.drop("id",axis=1,inplace=True)
#重复值检查,any(),一旦有重复值,就返回True
data.duplicated().any()
#删除重复的数据
data.drop_duplicates(inplace=True)
#查看各类别的数据条数
#print(data['Species'].value_counts())
#print(data)

#数据集拆分成训练集和测试集
#1、提取每个类别鸢尾花的数量
t0 = data[data['Species']==0]
t1 = data[data['Species']==1]
t2 = data[data['Species']==2]
 
#打乱顺序,random_state ,记住打乱的顺序
t0 = t0.sample(len(t0),random_state=0)
t1 = t1.sample(len(t1),random_state=0)
t2 = t2.sample(len(t2),random_state=0)
train_X = pd.concat([t0.iloc[:40,:-1],t1.iloc[:40,:-1],t2.iloc[:40,:-1]],axis=0)
train_Y = pd.concat([t0.iloc[:40,-1],t1.iloc[:40,-1],t2.iloc[:40,-1]],axis=0)
test_X = pd.concat([t0.iloc[40:,:-1],t1.iloc[40:,:-1],t2.iloc[40:,:-1]],axis=0)
test_Y = pd.concat([t0.iloc[40:,-1],t1.iloc[40:,-1],t2.iloc[40:,-1]],axis=0)
#print(train_X.shape)
#print(train_Y.shape)
#print(test_X.shape)
#print(test_Y.shape)
#进行训练与测试
knn = KNN(k=3)
#进行训练
knn.fit(train_X,train_Y)
#进行测试
result = knn.predict(test_X)
#display(result)
#display(test_Y)
#查看预测结果
#display(np.sum(result == test_Y))
r=np.sum(result == test_Y)
print("测试集的正确率:{:.2f}".format(knn.score(r,test_Y)))

 

四:测试用例设计及调试过程截屏

部分数据集:

原生python实现knn分类算法(鸢尾花数据集)     原生python实现knn分类算法(鸢尾花数据集)

实验结果:

原生python实现knn分类算法(鸢尾花数据集)

五:总结

  本次实验中主要问题是数据结构的混肴,knn实现类中,经过多次列表生成、嵌套,容易造成对数据结构的混淆,从而出现下标维数错误等错误,只需debug查看数据结构或者直接print输出每步内容观察即可。

  本次实验参照了大量CSDN中的资料,自己只是理解了knn算法,对于代码的实现大多参照网上资料,而且该方法计算量较大,因为对每一个待分类的文本都要计算它到全体已知样本的距离,才能求得它的K个最近邻点。该算法比较适用于样本容量比较大的类域的自动分类,而那些样本容量较小的类域采用这种算法比较容易产生误分。