# -*- coding: cp936 -*-
from numpy import *
import operator
from os import listdir
import time
#创建数据集
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
#inX:用于分类的输入向量
#dataSet:训练样本集
#labels:标签向量
#k:用于选择最近邻居的数目
def classify0(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0]#shape函数的功能是查看矩阵或数组的维数,也就是说知道有几个训练数据,所以shape[0]表示得到数组的行数,它在这里是4
diffMat=tile(inX,(dataSetSize,1))-dataSet#tile()函数是numpy模块中的函数,它将原来的一个数组,扩充成了4个一样的数组。diffMat得到了目标与训练数值之间的差值
sqDiffMat=diffMat**2#diffMat中的每一个元素(上一行代码计算出来的目标数据集与每个训练数据集的各个特征值(这里只有两个)之间的差值)进行平方
sqDistance=sqDiffMat.sum(axis=1)#把上一行代码中的平方项按’列‘(?sqDiffMat数组的第一列加第二列)进行相加
distance=sqDistance**0.5#开根号,计算出距离啦!
sortedDistIndicies=distance.argsort()#升序排列,得到一个升序排列的矩阵,argsort函数返回的是数组值从小到大的索引值
#下面进行选择距离最小的k个点
classCount={}#这是一个字典,用于计数,即用于存储不同标签(labels)出现的次数
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]]
#从前面已经排好序的矩阵中选择距离最小的k个点并获得这k个点所对应的标签,也就是前k个点,k是你在使用时具体输入的,书上k是3,所以就提炼出了前3个训练数据集的标签
#sortedDistIndicies[i]是索引值,正好与label的值的索引是对应的
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1#classCount[voteIlabel]是个变量名,别多想,然后voteIlabel是前k个,也就是距离最小的k个训练数据集所对应的标签(labels),现在利用dict.get(key, default=None)函数来统计这k个的标签出现的次数,key就是dict中的键voteIlabel,如果不存在则返回一个0并存入dict(这里的dict就是classCount),如果存在则读取(get)当前值(标签),并在classCount中加1
#最后得到的classCount{}应该是类似这种样子的:classCount{'A':3,'B':0}
#print voteIlabel
#排序
sortedClassCount=sorted(classCount.iteritems(),#这里使用了sorted()函数sorted(iterable, cmp=None, key=None, reverse=False),iteritems()将classCount这个dict分解为元组列表(一个list包含两个tuple):[('B', 2), ('A', 1)]
key=operator.itemgetter(1),reverse=True)#operator.itemgetter(1)表示按照第二个元素的次序对元组进行排序,因为编程语言的index(索引)从0开始
#print sortedClassCount
return sortedClassCount[0][0]#返回出现次数最多的的标签,这也就是最终的分类(label)结果
def file2matrix(filename):
fr=open(filename)
arrayOLines=fr.readlines()#一次读取整个文本数据,并且readlines() 自动将文件内容分析成一个行的列表,该列表可以由 Python 的 for ... in ... 结构进行处理。比readline()快 ,下面的img2vector就是使用的readline(),因为要逐行逐个读取,可以对比一下
#print arrayOLines#打印出一大堆,电脑要卡死的节奏
numberOfLines=len(arrayOLines)
returnMat=zeros((numberOfLines,3))#文件有几行就是几行,设置为3列(可调)
#到这里
classLabelVector=[]
index=0
for line in arrayOLines:
line=line.strip()#去掉回车符,(使上下的数据紧凑在一起) ,括号里面是不太对的理解,是去掉那个超级大的一行的列表中的回车符“\n” ,然后一行一行的处理,最后才得到所看到的上下的数据紧凑在一起的数据
#print line# 这玩意儿根本刹不住车!果断注释掉!
listFromLine=line.split('\t')#分成了4列数据,得到了4个列表
#print listFromLine#这玩意儿也刹不住车,密密麻麻的,注释掉!
returnMat[index,:]=listFromLine[0:3] #前3个列表元素是爱伦要的特征,取出来去填充returnMat
classLabelVector.append(int(listFromLine[-1]))#是1 or 2 or 3,填入得到分类标签向量
print classLabelVector
index+=1#继续迭代
return returnMat,classLabelVector
def autoNorm(dataSet):
minVals=dataSet.min(0)
#print minVals
maxVals=dataSet.max(0)
#print maxVals
ranges=maxVals-minVals
#print ranges
normDataSet=zeros(shape(dataSet))#先创建一个(0)返回矩阵,维度与DatSet一样
#print normDataSet
m=dataSet.shape[0]#获取dataSet的行数
normDataSet=dataSet-tile(minVals,(m,1))
normDataSet=normDataSet/tile(ranges,(m,1))
return normDataSet,ranges,minVals
def datingClassTest():
hoRatio=0.10
datingDataMat,datingLabels=file2matrix('datingTestSet.txt')#读取所有的数据
normMat,ranges,minVals=autoNorm(datingDataMat)#归一化
m=normMat.shape[0]#获取一共有多少行数据
numTestVecs=int(m*hoRatio)#取全部数据的10%用作测试,这里一共有numTestVecs个测试数据
errorCount=0.0
for i in range(numTestVecs):
classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],\
datingLabels[numTestVecs:m],3) #调用classify0函数,第一项是10%的测试数据,第二项是90%的训练数据,第三项是90%的训练数据所对应的标签,第四项是k的取值
print "the classifier came back with : %d,the real answer is : %d" %(classifierResult,datingLabels[i])
if (classifierResult!=datingLabels[i]):errorCount+=1.0#测试结果不等于已知结果,错误个数加1
print "the total error rate is: %f" %(errorCount/float(numTestVecs))#计算错误率
def classifyPerson():
resultList=['not at all','in small doses','in large doses']#构建返回的标签向量,这3个标签分别与第1,2,3类相对应,与最后一行代码呼应,海伦之前已经有了1000次的数据,所以我认为那个datingTestSet文件是本程序的结果汇总得到的答案,本身参与程序的是datingTestSet2文件,书中的27页代码清单2-4文件名写错了
percentTats=float(raw_input("percentage of time spent playing video game?"))
ffMiles=float(raw_input("frequent flier miles earned per year?"))
iceCream=float(raw_input("liters of ice cream consumed per year?"))
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt')
normMat,ranges,minVals=autoNorm(datingDataMat)
inArr=array([ffMiles,percentTats,iceCream])#创建目标特征值,这是classify0的第一个参数
classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3)#新输入的目标变量也需要进行归一化
print classifierResult
print "you will probably like this person:", resultList[classifierResult-1]#索引从0开始,索引减去1才能索引到对应的resultList
#这里综合调用了前面所构造的各个函数
def img2vector(filename):
returnVect=zeros((1,1024))#创建1行1024列的二维数组
fr=open(filename)#打开二进制图像文件
for i in range(32): #i
lineStr=fr.readline()#readline()每次读取一行,每迭代一次就读取一行,比readlines()慢得多,readline()返回的是一个字符串对象,保存当前行的内容
#print int(lineStr[0])
for j in range(32):
returnVect[0,32*i+j]=int(lineStr[j])
return returnVect
def handwritingClassTest():
time_start=time.time()#计时开始
hwLabels=[]
trainingFileList=listdir('trainingDigits')#将trainingDigits目录中的文件内容存储在训练列表"trainingFileList"中
m=len(trainingFileList)#得到目录中有多少文件,并将其存储在变量m中
trainingMat=zeros((m,1024))#创建一个m行1024列的训练矩阵,该矩阵的每行数据存储一个图像
for i in range(m):
fileNameStr=trainingFileList[i]#依次获取每个文件的名字
fileStr=fileNameStr.split('.')[0]#将文件名分割成两部分并只取第一部分。第二部分是后缀格式,我们不需要它
classNumStr=int(fileStr.split('_')[0])#获取类名(1-9)
hwLabels.append(classNumStr)#将类名依次添加到hwLabels里面
trainingMat[i,:]=img2vector('trainingDigits/%s'%fileNameStr)#打开目录中的每一个文件并添加到trainingMat这个矩阵中去
#print trainingMat
#下面对testDIGITS执行类似的操作
testFileList=listdir('testDigits') #将testDigits目录中的文件内容存储在测试列表"testFileList"中
errorCount=0.0
mTest=len(testFileList)#得到目录中有多少文件,并将其存储在变量mTest中
for i in range(mTest):
fileNameStr=testFileList[i]#依次获取每个文件的名字
fileStr=fileNameStr.split('.')[0]
classNumStr=int(fileStr.split('_')[0])
vectorUnderTest=img2vector('testDigits/%s'%fileNameStr)#这里和上面不同之处在于没有将读取的文件载入到矩阵中,而是要利用分类器进行测试,继续向下看
classifierResult=classify0(vectorUnderTest,trainingMat,hwLabels,3)#调用分类器进行测试
print "the classifier came back with: %d,the real answer is : %d"%(classifierResult,classNumStr)
if (classifierResult!=classNumStr):errorCount+=1
print "\nthe total number of errors is:%d"%errorCount
print "\nthe total error rate is :%f"%(errorCount/float(mTest))
time_end=time.time() #计时结束
print time_start-time_end,
print "s"
第二章kNN完整代码注释
最新推荐文章于 2021-09-19 19:59:55 发布