根据在《机器学习实战》这本书的代码和演示实例,给出KNN算法的实现细节,其中也包括一些数据分析中常用的用法的构建。
1.KNN分类器构建
KNN的步骤(下方函数也是按照此过程编写):
- (1) 计算已知类别数据集中的点与当前点(x)之间的距离;
- (2) 按照距离递增次序排序;
- (3) 选取与当前点距离最小的k个点;
- (4)确定前k个点所在类别的出现频率;
- (5) 返回前k个点出现频率最高的类别作为当前点的预测分类(多数选举原则)
#四个参数,分别是待判定的向量x,训练集,标签,k值
def classify0(inX,dataSet,lables,k):
# 计算dataSet第一维的长度,一般来说,这里计算的就是实例个数
#请注意,这里的0是数组中的用法,指的是0维,这是计算机中的数组特有的计算起点
dataSetSize=dataSet.shape[0]
#这一步做了非常多的事情:
# 1.title将向量x展开为了dataSetSize行的inX数组
# 2.展开后的向量与原先的矩阵进行减法,避免了for循环来进行减法
diffMat=tile(inX,(dataSetSize,1))-dataSet
sqDiffMat = diffMat ** 2 #矩阵中的每个元素都平方
sqDistances = sqDiffMat.sum(axis=1) #按照行加起来,得到一个新的矩阵
distances = sqDistances ** 0.5 #数组中的每个元素都开方
sortedDistIndicies = distances.argsort() #对距离进行排序,输出的是下标,注意这个用法,对后面查找类别有好处
classCount = {} #这里是一个字典,用于存储类别以及类别所出现的次数
#寻找最近的k个点
for i in range(k):
voteLable = lables[sortedDistIndicies[i]] #因为sortedDistIndicies存储的是坐标,直接对应在labels中找到的就是类别
#字典的get()函数,第一个参数为你想要在字典中查找的key,第二个参数是缺省值设定,这里设定了是0。
#如果存在这个想找的key,就返回这个key的value值;如果不存在,就返回设定的缺省值
#例如2类,出现过1次,就让2类+1,没有出现过就是设定为0次,随后+1
#简直美妙的函数
classCount[voteLable] = classCount.get(voteLable, 0) + 1
#获取最后判定的类别
#sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
#不会更改源数据:内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作
#字典的iteritems()函数返回一个迭代器,使得字典可适用于sorted方法
#key参数的作用是指定可迭代对象中的一个元素来进行排序;operator模块提供的itemgetter,定义了一个函数,用去取出在迭代对象中的指定位置
#reverse=True代表降序排序,False升序
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
return sortedClassCount[0][0]
2.程序2中读取文本文件,然后获得numpy矩阵的方法函数
注意:默认最后一列为标签列,且性质为int类型,如不符合需要进行相应的更改
# 程序2中的读取约会文本数据的程序
# 也是一个通用的,读取文本数据的程序(与数据格式相类似的)
# 输入:文件名或者文件的路径;col:需要从文本数据中读取的列数
# 文件读取两次的问题需要注意应该是由于操作对当前游标的位置问题,具体代码中有解释
def file2matrix(filename,col):
fr = open(filename)
numberOfLines = len(fr.readlines()) #获取实例的长度/数量
returnMat = zeros((numberOfLines,col)) #准备一个矩阵,用于存放数据
classLabelVector = [] #一个用于存放标签的列表
index = 0
fr=open(filename) #这里貌似是因为,len文件的长度后,光标就已经到文本的末尾了,再继续按行读取就无法读取到任何数据
for line in fr.readlines(): #按行读取
#strip()方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列
line = line.strip()
listFromLine = line.split('\t') #按照每遇到一个tab键进行分割
returnMat[index,:] = listFromLine[0:col] #将对应的值放入矩阵
classLabelVector.append(int(listFromLine[-1])) #对应的标签放去矩阵
index += 1 #矩阵索引+1
return returnMat,classLabelVector
3.小结
这篇文章主要是给出了KNN算法在python环境下的实现细节,由于python的一些优越的特性,使得进行循环计算距离的时候,能够轻松的实现,这主要是依赖于numpy库对于矩阵的计算的良好支撑。但是实际上,本质上构造的分类器还是一个线性的结构,与for循环遍历计算没有太大的区别。后续我会根据《统计学习方法》一书中的KD树来构建相应的快速循环查找的代码实现细节。