k-近邻算法伪代码
本文摘于《机器学习实战》中的案例,并对其中的代码进行更新与注释。
对位置类别属性的数据集中的每个点依次执行以下操作:
- 计算已知类别数据集中的点与当前点之间的距离;
- 按照距离递增次序排序;
- 选取与当前点距离最小的k个点;
- 确定前k个点所在类别的出现频率;
- 返回前k个点出现频率最高的类别作为当前点的预测分类。
例题描述
约会网站使用k-近邻算法,以便更好地帮助用户将匹配对象划分到确切的分类中。已知对象分类有三种:用户不喜欢的人、对用户魅力一般的人、对用户极具魅力的人。
现有数据文本文件“datingTestSet2.tst”,每个样本数据占据一行,总共有1000行。样本主要包含以下三个特征:
- 每年获得的飞行常客里程数
- 玩视频游戏所消耗时间百分比
- 每周消费的冰淇淋公升数
有需要数据者请回复
准备数据
(1)格式转化
在将上述特征数据输入到分类器之前,必须将待处理数据的格式改变为分类器可以接受的格式,即训练样本矩阵与类标签向量。
import numpy as np
#将待处理的数据格式处理之后,输出为训练样本矩阵与类标签向量
def file2matrix(filename):
fr=open(filename)
#读取文件所有行
arrayOLines=fr.readlines()
numberOfLines=len(arrayOLines)
#创建一个所需要的训练样本矩阵,其shape为(文件所有行,3)
returnMat=np.zeros((numberOfLines,3))
#创建一个类标签向量
classLabelVector=[]
index=0
for line in arrayOLines:
#strip()用于移除字符串中头尾指定的字符(默认为空格或换行符)或字符序列
line=line.strip()
#按指定字符‘\t’对字符串进行切片
listFormLine=line.split('\t')
returnMat[index,:]=listFormLine[0:3]
classLabelVector.append(int(listFormLine[-1]))
index+=1
return returnMat,classLabelVector
(2)归一化数值
现有如下四组数据,若计算样本3和样本4之间的距离,即利用空间点的距离公式(自行列出),很容易发现数字差值最大的属性对计算的结果影响也最大,也就是说,每年获取的飞行常客里程数对于计算结果的影响将大于表中的其他两个特征。但是规定这三种特征是同等重要的。因此影响特征值归一化,即将三个特征的取值范围处理为到1或者-1之间。
归一化核心公式:newValue=(oldValue-min)/(max-min)
玩游戏所耗时间百分比 | 每年获得的飞行常客里程数 | 每周消费的冰淇淋公升数 | 样本分类 | |
1 | 0.8 | 400 | 0.5 | 1 |
2 | 12 | 134000 | 0.9 | 3 |
3 | 0 | 20000 | 1.1 | 2 |
4 | 67 | 32000 | 0.1 | 2 |
#归一化特征值
def autoNorm(dataset):
#参数0使得函数可以从列中选取最小值
minVals=dataset.min(0)
maxVals=dataset.max(0)
ranges=maxVals-minVals
normDataSet=np.zeros(np.shape(dataset))
normDataSet=dataset-np.tile(minVals,(dataset.shape[0],1))
normDataSet=normDataSet/np.tile(ranges,(dataset.shape[0],1))
return normDataSet,ranges,minVals
k-近邻算法代码
根据伪代码结合实际问题灵活编写
def classify0(inx,dataset,labels,k):
datasize=dataset.shape[0]
diffMat=np.tile(inx,(datasize,1))-dataset
sqDiffMat=diffMat**2
#axis=1代表横轴,即列表每行求和
sqDistance=sqDiffMat.sum(axis=1)
distance=sqDistance**0.5
#.argsort()返回列表值从小到大的索引值
sortedDistIndicies=distance.argsort()
classCount={}
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
#items()返回字典的元组列表,逆序=降序;sorted()函数返回的是列表副本,sort()是对自己的操作,无返回值
sortedClassCount=sorted(classCount.items(),key=lambda x:x[1],reverse=True)
return sortedClassCount[0][0]
测试算法
在这里,我们使用已提供的数据的90%作为训练样本来训练分类器,而使用其余的10%数据去测试分类器。
def datingClassTest():
#用于测试的样本比例
hoRatio=0.10
datingDatMat,datingLabels=file2matrix('datingTestSet.txt')
normMat,range,minVals=autoNorm(datingDatMat)
m=normMat.shape[0]
errorcount=0.0
numTestVecs=int(m*hoRatio)
for i in range(numTestVecs):
classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],
datingLabels[numTestVecs:m,:],3)
print("the classifier came back with:{},the real answer is:{"
"}".format(classifierResult,datingLabels[i]))
if classifierResult!=datingLabels[i]:
errorcount+=1.0
print("the total error rate is:{}".format(errorcount/float(numTestVecs)))
使用算法
def calssifyPerson():
resultList=['not at all','in samll doses','in large doses']
percentTate=float(input("percentage of time spent playing video games?"))
ffMiles=float(input("frequent flier miles earned per year?"))
iceCream=float(input("liters of ice cream consumed per year?"))
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
normMat,ranges,minVals=autoNorm(datingDataMat)
inArr=np.array([ffMiles,percentTate,iceCream])
#对带预测样本inArr进行归一化
inArr=(inArr-minVals)/ranges
classifierResult=classify0(inArr,normMat,datingLabels,3)
print("u will probably like this person:{}".format(resultList[
classifierResult-1]))