K-邻近算法(KNN)的工作原理是:已知一个带标签的已分类数据集合,输入未分类的新数据之后,计算新数据到数据集中的每个数据的欧几里得距离,筛选出前k个最近的点,选择这k个点中出现次数最多的分类,即作为新数据的分类标签
问题描述:已知4个带标签的坐标点A1(1.0,1.1),A2(1.0,1.0),B1(0.0,0.0),B2(0.0,0.1),输入一个新数据后,输出它是属于A类还是B类
实现流程:
1、导入数值计算库numpy和备胎模块opetator
#KNN.py
from numpy import *
import operator
2、创建带标签的数据,之后直接调用这个方法就能返回我们的数据集
#用于创建数据
def create_data_set():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group, labels
3、KNN算法的核心实现
def classify0(in_x, data_set, labels, k):
data_set_size = data_set.shape[0]#获取数组的第一维度的维数,即获取数据量
diff_mat = tile(in_x, (data_set_size,1)) - data_set#计算距离,首先获取坐标差
sq_diff_mat = diff_mat**2#对坐标差进行分别平方
sq_distances = sq_diff_mat.sum(axis=1)
distances = sq_distances**0.5
sorted_dist_index = distances.argsort()
class_count={}#创建一个字典
for i in range(k):
vote_i_label = labels[sorted_dist_index[i]]
class_count[vote_i_label] = class_count.get(vote_i_label,0)+1
sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1),reverse=True)
return sorted_class_count[0][0]
代码笔记:
A.shape(0)函数获取数组A的第一个维度,常常用于计算数据量
tile(A,3)函数将A重复3次,若第二个参数输入的是数组的形式,如tile(A,(3,2)),那就是生成一样的三组,每组都两个A这样
命名规范上,python方法名和变量名都是小写,单词之间用下划线分隔开来
书写习惯上,运算符+-*/=左右都加一个空格(若=在方法的参数列表中则不加,后面如果是字符加一个空格(若是数字则不加)
计算欧几里得距离的处理方法也太高效简洁了。把输入数据重复四次,四个数据点合并成矩阵处理,之后减去带标签的原来那四个点,然后对结果矩阵中的每个元素进行分别做平方运算 ,sum(a,axis=1)对矩阵a的行向量进行相加合并,axis=0则会对列向量进行相加合并;计算行向量的和的结果之后开根号计算距离
.argsort()函数获取排序序列,它会比较数组中的元素,将其从小到大做一个排序,提取其对应的index(索引),然后返回此索引排序结果
for i in range(k):循环处理 i的值0~(k-1)
Python中的花括号{}代表dict字典数据类型,字典是Python中的映射类型,由键值对组成,没有特殊的顺序;使用.get(a,0) 函数返回指定键a的值,如果值不在字典中返回默认值,第二个参数表示我设定的默认值是0;使用.items()方法获取字典中所有项(仍然是无序的)
sorted函数,第一个参数表示要排序的列表主体,第二个参数key表示排序的标杆,第三个参数reverse为True的话表示要逆序成从大到小进行排列。sorted函数以数组的形式返回排好的序列,(数组是有序的,列表是无序的),所以后续可以用index索引的方式获取想要的信息
使用operator模块的itemgetter(0)方法 ,获取键值对的第1个值
测试代码:
在命令行中切换到KNN.py文件所在目录并进入python开发环境。导入KNN.py文件后首先调用create_data_set方法创建带标签的数据集,接着调用classify0方法,参数依次为要测试的数据、数据集及其标签、设定的k值,在代码无误的情况下就能获取测试数据的分类了
BUG记录:
在调用classify0方法的时候遇到过第30行object is not iterable的报错信息,随即想起在operator模块的学习资料里描述过,它常见于使用在某些迭代器中被调用。那报错的原因就应该是operator的作用对象是不可迭代的,检查发现果然是在对字典数据类型调用items()方法获取其列表时写错了