强迫症一点点。代码注释很详细。第一次用这个。。。。
#k-近邻算法示例代码
from numpy import *
import operator
from os import listdir #列出给定目录的文件名
import matplotlib
import matplotlib.pyplot as plt
#定义样本
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
#简单分类器
def classify0(inX,dataSet,labels,k): #4个输入参数:用于分类的输入向量是inX,输入的训练样本集为dataSet,向量标签为labels,k表示用于选择最近邻居的数目,标签向量数域矩阵dataSet的行数相同
dataSetSize=dataSet.shape[0] #.shape[0]返回的是矩阵的行数(一维的长度),.shpae[1]返回的是矩阵的列数(二维的长度)
diffMat=tile(inX,(dataSetSize,1))-dataSet #tile函数参数:inX这个整体,在行上(行变)重复dataSetSize次,列上重复1次
sqDiffMat=diffMat**2 #数组类型(array)的平方表示每个元素进行单独平方,而矩阵类型(matrix)的平方表示矩阵的数乘运算
sqDistances=sqDiffMat.sum(axis=1) #axis=1矩阵中按行的方向相加并返回每一行的和值(行相同,列不同),axis=0是在列上操作(列相同,行不同)
distances=sqDistances**0.5 #距离的平方进行开方
sortedDistIndicies=distances.argsort() #argsort(a)返回a数组元素从小到大排序后的原来元素的下标排序
classCount={} #定义一个空字典
for i in range(k): #选择距离最小的k个点
voteIlabel=labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
sortedClassCount=sorted(classCount.items(), #classCount.items()函数返回字典的一个迭代器,reverse的布尔值设置是否为倒序,默认为从小到大,itemgetter是获取第1个域的值
key=operator.itemgetter(1),reverse=True)#这里对classCount.iteritems()作修改,python3中将迭代器改为:items()
return sortedClassCount[0][0]
#文件解析
def file2matrix(filename):
fr=open(filename)
arrayOLines=fr.readlines() #将文件列表化,每一行为一个列表元素
numberOfLines=len(arrayOLines) #获得列表元素个数,即文件行数
returnMat=zeros((numberOfLines,3)) #np.zeros((n)):创建一个一维数组有n个元素,并初始化为0;np.zeros((n,m)):初始化一个n×m的数组类型矩阵,并初始化为0
classLabelVector=[] #声明一个空列表
fr=open(filename)
index=0
for line in arrayOLines:
line=line.strip() #strip()方法用于移除字符串头尾指定的字符(默认为空格或换行符)或字符序列,但不能移除串内的符号
listFromLine=line.split('\t') #以原文中的'\t'为标准划分字符数组元素,可设置第二个参数指定划分个数,从0开始计数1
returnMat[index,:]=listFromLine[0:3] #将每行的前三个放入矩阵,mat[index,:]是取第index行,mat[:,index]是取第index列
classLabelVector.append(int(listFromLine[-1])) #在列表末尾添加元素(此处添加的是字符串数组每个元素的最后一个)
index+=1 #矩阵行数加一移动
return returnMat,classLabelVector
#listFromLine=list(map(float,listFromLine)) #这句话可以将字符串类型的数字转换为标准数字类型
#归一化数值
def autoNorm(dataSet):
minVals=dataSet.min(0) #取得列中的最小值,minVals是一个数组
maxVals=dataSet.max(0) #取得列中的最大值,maxVals是一个数组
ranges=maxVals-minVals #取得范围值
normDataSet=zeros(shape(dataSet)) #初始化一个和给定数组类型矩阵一样形状的数组矩阵,元素全部为0
m=dataSet.shape[0] #得到数组矩阵的行数
normDataSet=dataSet-tile(minVals,(m,1)) #和下一行按照newValu=(oldValue-min)/(max-min)计算新的数组矩阵
normDataSet=normDataSet/tile(ranges,(m,1))
return normDataSet,ranges,minVals
#分类器对约会网站的测试代码
def datingClassTest():
hoRatio=0.10 #测试量10%
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt') #获得评估项目矩阵和结果列表
normMat,ranges,minVals=autoNorm(datingDataMat) #归一化评估数据
m=normMat.shape[0] #得到行数
numTestVecs=int(m*hoRatio) #到第几行测试结束
errorCount=0.0
for i in range(numTestVecs):#测试阶段
classifierResult=classify0(normMat[i,:],normMat[numTestVecs:m,:],\
datingLabels[numTestVecs:m],3)
print("The classifier came back with: %d ,the real answer is: %d"\
%(classifierResult,datingLabels[i]))
if(classifierResult!=datingLabels[i]):errorCount+=1.0
print("The total error rate is: %f"%(errorCount/float(numTestVecs)))
#datingClassTest()
#约会网站测试函数
def classifyPerson():
resultList=['not at all','in small does','in large does'] #字符列表,将标识数字转换为人可见分类(相当于第一个函数里面的Labels[])
percentTats=float(input("percentage of time spent playing vedio games?")) #以下三行输入检测数据
ffMiles=float(input('frequent filer miles earned per year?'))
iceCream=float(input('liters of icecream consumed per year?'))
datingDataMat,datingLabels=file2matrix('datingTestSet2.txt') #对样本进行检测,得到分类标准
normMat,ranges,minVals=autoNorm(datingDataMat) #归一化数据
inArr=array([ffMiles,percentTats,iceCream]) #将输入的数据列表化
classifierResult=classify0((inArr-minVals)/ranges,normMat,datingLabels,3) #将输入的数据归一化并对其分类处理
print('you will probably like this person: ',resultList[classifierResult-1]) #得到分类结果
#手写识别对象(数字转换为可读的向量形式,转换为矩阵识别)
def img2vector(filename):
returnVect=zeros((1,1024)) #向量格式图片存储数组类型矩阵
fr=open(filename)
for i in range(32):
lineStr=fr.readline() #分行读入为字符列表,每次只读一行
for j in range(32):
returnVect[0,32*i+j]=int(lineStr[j])
return returnVect
#手写数字识别系统的测试代码
def handwritingClassTest():
hwLabels=[] #创建一个列表,用于记录分类
trainingFileList=listdir('trainingDigits') #列出给定目录的文件名(不包括子目录,但是得到的文件名包含文件类型,即包括文件名的后缀)
m=len(trainingFileList) #获得给定目录下的文件数量
trainingMat=zeros((m,1024)) #创建一个m*1024的数组类型矩阵
for i in range(m): #文件遍历
fileNameStr=trainingFileList[i] #开始逐个访问文件名
fileStr=fileNameStr.split('.')[0] #将文件名和后缀名分开并去文件名
classNumStr=int(fileStr.split('_')[0]) #理解文件命名方式:1_111.txt,“1”表示中是手写的1,“111”表示这是第111个手写体样本,以“_”划分字符数组并取文件表示的数字是几,并将字符转换为int类型
hwLabels.append(classNumStr) #在列表末尾添加取到的数字
trainingMat[i,:]=img2vector('trainingDigits/%s'%fileNameStr) #将得到的0,1数字列表赋值给trainingMat的第i行,后面的参数列表可以等同于print()的输出列表形式。
testFileList=listdir('testDigits') #再打开测试列表读取该目录下的所有文件名
errorCount=0.0 #分类器出错数量
mTest=len(testFileList) #获取测试列表的长度
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) #trainingMat和hwLabels里面的东西是一一对应的,是0,1序列和其所表示的数字的映射关系。这里挑分类器认为最接近的三个结果
print("the classifier came back with: %d,the real answer is: %d"%(classifierResult,classNumStr)) #打印分类器判断结果和真实结果
if(classifierResult!=classNumStr):errorCount+=1.0 #错误数目计算
print("\nthe total number of errors is:%d"%errorCount) #打印判断错误的数量
print("\nthe total number of rate is:%f"%(errorCount/float(mTest))) #打印错误率