您的位置:首页 > 编程语言

第二章kNN完整代码注释

2018-01-19 16:41 99 查看
# -*- 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"
                  
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐