K-近邻算法概述
采用不同特征值之间的距离方法进行分类
优点:精度高、对异常值不敏感、无数据输入假定
缺点:计算复杂度高,空间复杂度高
适用范围:数值类型和标称型
有一个训练样本集,每个样本都有标签,输入新数据,将新数据和旧的进行比较,然后提取最相似数据的标签。一般是选择K个,不大于20,最后选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
电影:亲吻次数、打斗次数来分类爱情片还是动作片
数据如下:
电影名称 | 打斗镜头 | 亲吻镜头 | 电影类型 |
California Man | 3 | 104 | 爱情片 |
He's Not Really into Dues | 2 | 100 | 爱情片 |
Robo Salyer 3000 | 99 | 5 | 动作片 |
计算出新电影与旧电影的距离,按照距离递增排序,找到k个距离最近的电影,它们的类型来决定未知电影的类型
2.1.1 准备:适用Python导入数据
代码:kNN.py
from numpy import *
import operator
def createDataSet():
group = array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels = ['A','A','B','B']
return group,labels
group,labels = kNN.createDataSet()
2.1.2 从文本文件中解析数据
伪代码
对未知类别属性的数据集中的每个点依次执行以下操作:
- 计算已知类别数据集中点与当前点之间的距离
- 按照距离递增排序
- 选取与当前点距离最小的k个点
- 确定前k个点所在类别的出现频率
- 返回前K个点出现频率最高的类别作为当前点的预测分类
def classifyKNN(inX,dataSet,labels,k):
dataSetSize = dataSet.shape[0]
# 距离计算
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):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel ] = classCount.get(voteIlabel ,0) + 1
# 排序
sortedClassCount = sorted(classCount.iteritems(),key=operator.itegetter(1),reverse=True)
return sortedClassCount[0][0]
kNN.classifyKNN([0,0],group,labels,3)
使用的是欧式距离
d= sqrt( (xA0-xB0)^2 + (xA1-xB1)^2)
点(0,0)与(1,2)的距离是:sqrt(5)
np.tile(testData, (rowSize, 1)) 是将 testData 这个数据扩展为 rowSize 列,这样能避免运算错误;
sorted(count.items(), key=operator.itemgetter(1), reverse=True) 排序函数,里面的参数 key=operator.itemgetter(1), reverse=True 表示按照 count 这个字典的值(value)从高到低排序,如果把 1 换成 0,则是按字典的键(key)从高到低排序。把 True 换成 False 则是从低到高排序。
完整的的代码
#!/user/bin/env python
#-*- coding:utf-8 -*-
import numpy as np
import operator as opt
# 标准化数据
def normData(dataSet):
maxVals = dataSet.max(axis=0)#按列获取最大值,并返回数组
minVals = dataSet.min(axis=0)
ranges = maxVals - minVals
retData = (dataSet - minVals) / ranges
return retData, ranges, minVals
def kNN(dataSet, labels, testData, k):
distSquareMat = (dataSet - testData) ** 2 # 计算差值的平方
distSquareSums = distSquareMat.sum(axis=1) # 求每一行的差值平方和,axis=0则按列计算
distances = distSquareSums ** 0.5 # 开根号,得出每个样本到测试点的距离
sortedIndices = distances.argsort() # 排序,得到排序后的下标
indices = sortedIndices[:k] # 取最小的k个
labelCount = {} # 存储每个label的出现次数,出现次数最多的就是我们要选择的类别
for i in indices:
label = labels[i]
labelCount[label] = labelCount.get(label, 0) + 1 # 次数加一,使用字典的get方法,第一次出现时默认值是0
sortedCount = sorted(labelCount.items(), key=opt.itemgetter(1), reverse=True) # 对label出现的次数从大到小进行排序
return sortedCount[0][0] # 返回出现次数最大的label
if __name__ == "__main__":
dataSet = np.array([[2, 3], [6, 8]])#训练集
normDataSet, ranges, minVals = normData(dataSet)
labels = ['a', 'b']#训练集分别为a和b类
testData = np.array([3.9, 5.5])#测试数据
normTestData = (testData - minVals) / ranges#同样需要将测试数据标准化
result = kNN(normDataSet, labels, normTestData, 1)#k=1
print(result)