k-近邻算法(k-NN)及其Python实现
算法思想:
给定一个训练数据集,对新的输入实例,在训练数据集中找到与该实例最邻近的K个实例(K个邻居), 这K个实例的多数属于某个类,就把该输入实例分类到这个类中。算法流程:
- 计算已知类别数据集中的点与当前点之间的距离;
- 按照距离递增次序排序;
- 选取与当前点距离最小的k个点;
- 确定前k个点所在类别的出现频率;
- 返回前k个点出现频率最高的类别作为当前点的预测分类。
优缺点:
优点:简单易懂;
缺点:时间复杂度高、k值的选取需要专家经验改进方法:
降低k-NN时间复杂度,提高搜索效率,可以通过KD-Tree进行优化,详细介绍可以参考KD-Tree详解。
- 实验数据集:
Iris 数据集,下载地址:https://archive.ics.uci.edu/ml/datasets.html - Python代码实现:
from numpy import *
import operator
#文件读取
def file2matrix(filename, attribute_num): #传入参数:文件名,属性个数
fr = open(filename)
arrayOLines = fr.readlines()
numberOfLines = len(arrayOLines) #统计数据集行数(样本个数)
returnMat = zeros((numberOfLines, attribute_num))
classLabelVector = [] #分类标签
index = 0
for line in arrayOLines:
line = line.strip() #strip() 删除字符串中的'\n'
listFromLine = line.split(',') #将一个字符串分裂成多个字符串组成的列表,不带参数时以空格进行分割,当代参数时,以该参数进行分割
returnMat[index, : ] = listFromLine[0:attribute_num] #读取数据对象属性值
classLabelVector.append(listFromLine[-1]) #读取分类信息
index += 1
return returnMat, classLabelVector #返回值:数据集,分类信息
#数据归一化
def autoNorm(dataSet): #传入参数:数据集
minVals = dataSet.min(0) #数据集每一行的最小值 1Xattribute_num
maxVals = dataSet.max(0) #数据集每一行的最大值 1Xattribute_num
ranges = maxVals - minVals
normDataSet = zeros(shape(dataSet))
m = dataSet.shape[0] #统计数据集行数
normDataSet = dataSet -tile(minVals, (m, 1))
normDataSet = normDataSet/tile(ranges, (m, 1))
return normDataSet
#return normDataSet, ranges, minVals #返回值:归一化后数据集,每列的最大值与最小的差值,每列的最小值
#k-NN算法
def classify0(testData, trainDataSet, labels, k): #传入参数:测试样本,训练样本数据集,分类标签,k-近邻中的k值
dataSetSize = trainDataSet.shape[0] #统计训练样本数据集行数
diffMat = tile(testData, (dataSetSize, 1)) - trainDataSet #对应属性值求差 #欧式距离计算测试样本与所有训练样本的距离
sqDiffMat = diffMat**2 #差值平方
sqDistances = sqDiffMat.sum(axis=1) #对每一行求和
distance = sqDistances**0.5 #开根号 #得到测试样本与所有训练样本的欧式距离
sortedDistIndicies = distance.argsort() #测试样本与训练样本的距离按照升序排列
classCount = {}
for i in range (k): #选择与测试样本距离最小的k个训练样本点
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 #统计类标签出现的个数
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1), reverse=True) #reverse参数是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时将按降序排列。
return sortedClassCount[0][0] #返回第一个值,即为出现次数最多的类标签
#测试
def datingClassTest():
hoRatio= 0.3 #原始数据集中,选取前30%作为待测样本,选取后70%作为训练样本
attribute_num = 4 #数据集样本点属性个数
k = 10 #k-NN,参数k,距离待测样本最近的k个样本点
oriDataSet, classLable = file2matrix('iris.txt', attribute_num) #读取原始数据集,以及分类标签
normDataSet = autoNorm(oriDataSet) #对数据集进行归一化处理
m = normDataSet.shape[0]
beginTestDataSet = int(m*hoRatio) #原始数据集中,选取前30%作为待测样本,选取后70%作为训练样本
errorCount = 0.0 #统计分类错误率
for i in range(beginTestDataSet):
classifierResult = classify0(normDataSet[i, :], normDataSet[beginTestDataSet:m, :], classLable[beginTestDataSet:m], k)
print "the classifier came back with: %s, the real answer is %s" %(classifierResult, classLable[i])
if (classifierResult!=classLable[i]):
errorCount += 1.0
print "the total error rate is: %f" %(errorCount/float(beginTestDataSet))
#return errorCount/float(beginTestDataSet)
def main():
datingClassTest()
if __name__ == '__main__':
main()