KNN最近邻法的基本思想:一个样本与数据集中的k个样本最相似, 如果这k个样本中的大多数属于某一个类别, 则该样本也属于这个类别。
步骤:

  1. 对测试集中的每个样本依次与训练集中的所有样本进行欧氏距离的计算 如原数组距离为 [0.3,,0.5,0.2,0.1,0.4,0.3,0.1];
  2. 将所有距离进行从小到大排序( [0.1,0.1,0.2,0.3,0.3,0.4,0.5]),并找出每个距离在排序之前的原数组索引位置([3,6,2,0,5,4,1]);
  3. 根据k值,选取距离最近的K个元素的索引,如k = 3,则选取(3,6,2)这k个索引;
  4. 根据这k个索引,寻找出对应标签y中这k个索引位置的值,如(1,1,2);
  5. 统计这个对应标签y中这k个索引位置的值出现的次数,如1出现了2次,2出现了1次
  6. 出现次数最多的那个值,则为判定的类别,如 1 的次数最多,则判定为 1
'''采用鸢尾花数据集
'''
import numpy as np
from sklearn.datasets import load_iris
import pandas as pd

#head 指定第几行为标题,默认为0,若无标题为NOne
data = pd.read_csv('Iris.csv',header = 0)
# data.head() # 首5行
# data.tail() #末尾5行
data.sample(10) # 默认随机抽取一条数据
# 将类别文本map映射数值类型
data['Species'] = data["Species"].map({"Iris-virginica":0,"Iris-setosa":1,"Iris-versicolor":2})
# 删除不需要的ID类
data.drop('Id',axis = 1,inplace = True)
data.duplicated().any() # 是否重复的数据
#删除重复的数据
data.drop_duplicates(inplace = True)
# 查看各个类别的鸢尾花具有多少记录
data["Species"].value_counts()

'''KNN方法实现
'''
class KNN:
    # 实现k近邻方法 分类 
    def __init__(self,k):
        '''初始化方法
        
        Parameters
        k: int
            邻居个数
        '''
        
        self.k = k
        
    
    def fit(self,X,y):
        '''训练方法
        
        Parameters
        X: 类数组类型,形状为:[样本数量,特征数量]
            带训练的样本特征(属性)
        
        y:类数组类型,形状为:【样本数量】
            每个样本的目标值(标签)
        
        '''
        self.X = np.array(X)
        self.y = np.array(y)
        
    def predict(self,X):
        '''根据参数传递的样本,对样本数据进行预测
        
        Parameters
        X: 类数组类型,形状为:[样本数量,特征数量]
            带训练的样本特征(属性)
        
        Return: 数组类型
            预测结果
        
        '''
        
        X= np.asarray(X)
        result = []
        # 对数组遍历,每次取数组中的一行数据
        for x in X:
            # 对于测试集中的每一个样本,依次与训练集中的所有样本求欧式距离
            dis = np.sqrt(np.sum((x-self.X)**2,axis = 1))  # 欧氏距离:矩阵中每行元素进行相加再平方,再更号。更号下【(x-X1)2 + x-X2)2+...+】
            print("dis",dis)
            
            # 返回数组排序后,每个元素在原数组中(排序之前)的索引
            index = dis.argsort()
            print("index",index)
            # 进行截断,只取前k个元素,【取距离最近的k个元素的索引】
            index= index[:self.k]
            print("index1",index)

            # self.y[index] 找出对应标签y中的值
            # np.bincount()返回数组中每个元素出现的次数,元素必须是非负的整数
            count = np.bincount(self.y[index])
            print("count",count)
            # 返回数组中,值最大元素对应的索引,索引就是我们判断的类别。也就是说y[index]中那个元素出现的最多就是哪个类别
            # 最大元素索引就是出现次数最多的元素
            result.append(count.argmax())
                       
        return np.asarray(result)

	 def predict2(self,X):
        '''根据参数传递的样本,对样本数据进行预测 ( 有权重的设置)
        
        Parameters
        X: 类数组类型,形状为:[样本数量,特征数量]
            带训练的样本特征(属性)
        
        Return: 数组类型
            预测结果
        
        '''
        
        X= np.asarray(X)
        result = []
        # 对数组遍历,每次取数组中的一行数据
        for x in X:
            # 对于测试集中的每一个样本,依次与训练集中的所有样本求欧式距离
            dis = np.sqrt(np.sum((x-self.X)**2,axis = 1))  # 欧氏距离:矩阵中每行元素进行相加再平方,再更号。更号下【(x-X1)2 + x-X2)2+...+】
            print("dis",dis)
            
            # 返回数组中元素在排序前的位置
            index = dis.argsort()
            print("index",index)
            # 进行截断,只取前k个元素,【取距离最近的k个元素的索引】
            index= index[:self.k]
            print("index1",index)

            # self.y[index] 找出对应标签y中的值
            # np.bincount()返回数组中每个元素出现的次数,元素必须是非负的整数 :使用weights考虑权重,权重为距离的倒数
            count = np.bincount(self.y[index],weights = 1/dis[index])
            print("count",count)
            # 返回数组中,值最大元素对应的索引,索引就是我们判断的类别
            # 最大元素索引就是出现次数最多的元素
            result.append(count.argmax())
                        
        return np.asarray(result)    


# 提取每个每个类别的鸢尾花数据
t0= data[data['Species']==0] 
t1= data[data['Species']==1] 
t2= data[data['Species']==2]
# 对每个类别数据进行洗牌
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) 

# 创建KNN对象,进行训练与测试
knn = KNN(k = 3)
# 进行训练
knn.fit(train_X,train_y)
# 使用不带权重的进行测试,获得结果
result = knn.predict(test_X)
display(result)
display(np.sum(result==test_y))# 预测正确的个数

# 使用带权重的进行测试
result2 = knn.predict2(test_X)
display(np.sum(result2 == test_y))   # 预测正确的个数

         
import matplotlib as mpl
import matplotlib.pyplot as plt
# 默认不支持中文,设置支持中文显示
mpl.rcParams['font.family'] = 'SimHei'  # 黑体
mpl.rcParams['axes.unicode_minus'] = False # 设置中文字体时,能够正常显示负号 -
# 绘制训练集中三类的散点图
# "Iris-virginica":0,"Iris-setosa":1,"Iris-versicolor":2
# 设置画布的大小
plt.figure(figsize = (10,10))
plt.scatter(x = t0['SepalLengthCm'][:40],y = t0['PetalLengthCm'][:40],color = 'r',label = 'Iris-virginica')
plt.scatter(x = t1['SepalLengthCm'][:40],y = t1['PetalLengthCm'][:40],color = 'g',label = 'Iris-setosa')
plt.scatter(x = t2['SepalLengthCm'][:40],y = t2['PetalLengthCm'][:40],color = 'b',label = 'Iris-versicolor')

# 绘制测试集中三类的散点图
right = test_X[result == test_y]
wrong = test_X[result != test_y]
plt.scatter(x = right['SepalLengthCm'],y = right["PetalLengthCm"],color = 'c',marker = 'x',label = 'right')
plt.scatter(x = wrong['SepalLengthCm'],y = wrong["PetalLengthCm"],color = 'm',marker = '>',label = 'wrong')

plt.xlabel("花萼长度")
plt.ylabel("花瓣长度")
plt.title("KNN分类结果显示")
plt.legend(loc = 'best')
plt.show()

使用knn运行mnist数据集并输出roc曲线 knn实现数据集分类_机器学习