目录
(3)测试代码:源数据为datingTestSet2.txt
一、算法主要适用情形
1、优缺点:精度高、对异常数值不敏感、无数据输入绑定,但是计算复杂度较高,空间复杂度高
2、适用类型:数值型、标称型
3、计算公式:
4、计算两点之间的距离:
二、算法主要实现思路
【注意:一般我们只选择样本数据中前k个最相似数据,通常k是不大于20的整数】
第一步:导入源数据,分离出特征值和标签
第二步:计算用于分类的输入向量与源数据文件特征向量之间的距离
第三步:对上述距离按照从小到大的顺序进行排序
第四步:算则距离最小的k个特征向量
第五步:找出这k个特征向量对应标签出现频率最高的作为分类结果
三、代码实现
1、引入numpy及后续读取目录需要引入的
#导入数据
from numpy import *
import operator
from os import listdir
2、创建数据集和标签
#创建数据集和标签
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
3、k-邻近分类算法
#k-近邻算法
#计算距离
#inX:用于分类的输入向量
#dataSet:输入的训练样本集
#labels:标签向量
#k:用于选择最近邻居的数目
def classify0(inX, dataSet, labels, k):
#=====计算距离=====
dataSetSize = dataSet.shape[0] #shape读取数据矩阵第一维度的长度--4
#[[-1. -1.1],[-1. -1. ],[ 0. 0. ],[ 0. -0.1]]
diffMat = tile(inX,(dataSetSize,1)) - dataSet #tile重复数组inX,有dataSetSize行 1个dataSet列,减法计算差值
#[[1. 1.21],[1. 1. ], [0. 0. ],[0. 0.01]]
sqDiffMat = diffMat**2 #**是幂运算的意思,这里用的欧式距离
sqDistances = sqDiffMat.sum(axis=1) #普通sum默认参数为axis=0为普通相加,axis=1为一行的行向量相加
distances = sqDistances**0.5
sortedDistIndicies = distances.argsort()#argsort返回数值从小到大的索引值(数组索引0,1,2,3)
classCount={}
#=====选择距离最小的k个点=====
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]] #根据排序结果的索引值返回靠近的前k个标签
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 #各个标签出现频率
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)#排序频率
return sortedClassCount[0][0]#找出频率最高的
4、测试(0,0)的分类结果
import knn
from numpy import *
#获得已知数据集
group,labels=knn.createDataSet()
a=knn.classify0([0, 0], group, labels, 2)
print(a)
四、应用一:使用k-邻近算法改进约会网站的配对效果
1、算法步骤
第一步:读取数据文件,把数据划分特征值矩阵和标签矩阵
第二步:;归一化特征值,将数字特征值转化到0到1的区间
第三步:按照上述k-邻近算法计算前k个与待求特征向量距离最近的特征向量,返回出现频率最高的分类标签
2、代码实现
(1)读取数据文件,把数据划分特征值矩阵和标签矩阵
#用来从文件中加载数据:把文件中的数据划分为特征值矩阵returnMat和标签矩阵classLabelVector分别返回
def file2matrix(filename):
fr=open(filename) #打开文件并且获取数据
arrayOLines=fr.readlines()
numberOfLines=len(arrayOLines)#读出数据行数
returnMat=zeros((numberOfLines,3)) #准备返回矩阵,numberOfLine行,3列
classLabelVector=[] #准备结果标签
index=0
for line in arrayOLines:
line=line.strip() #删除空白符
listFromLine=line.split("\t")#split指定分隔符对数据切片
returnMat[index,:]=listFromLine[0:3]#选取前3个元素(特征)存储在返回矩阵中
classLabelVector.append(int(listFromLine[-1])) #-1索引表示最后一列元素,位label信息存储在classLabelVector
index += 1
return returnMat,classLabelVector #返回矩阵和标签
(2)归一化特征值
#归一化特征值,将数字特征值转化到0到1的区间
#归一化公式 :归一化后的值=(当前值-最小值)/(最大值-最小值)
def autoNorm(dataSet):
minVals = dataSet.min(0)#存放每列最小值,参数0使得可以从列中选取最小值,而不是当前行
maxVals = dataSet.max(0)#存放每列最大值
ranges = maxVals - minVals
#[[0. 0.],[0. 0.],[0. 0.], [0. 0.]]
normDataSet = zeros(shape(dataSet))#初始化归一化矩阵为读取的dataSet格式
m = dataSet.shape[0]#m保存数据项数--4
# 特征矩阵是3x1000,min max range是1x3 因此采用tile将变量内容复制成输入矩阵同大小
#最小值minVals是0,利用tile()函数将变量内容复制成输入矩阵同大小tile(minVals, (m,1):[[0. 0.],[0. 0.],[0. 0.],[0. 0.]]
normDataSet = dataSet - tile(minVals, (m,1))
normDataSet = normDataSet/tile(ranges, (m,1))
return normDataSet, ranges, minVals
(3)测试代码:源数据为datingTestSet2.txt
#测试约会网站分类结果代码
def datingClassTest():
hoRatio = 0.10 #hold out 10%
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt') #分类特征值和标签并存为矩阵
normMat, ranges, minVals = autoNorm(datingDataMat) #归一化特征值
m = normMat.shape[0] #样本总数量
numTestVecs = int(m*hoRatio) #计算测试样本的数量;90%训练样本、10%测试样本
errorCount = 0.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)))
print (errorCount)
(4)运行上述函数,输出结果
五、应用二:手写识别算法
1、算法描述
第一步:准备数据:将图像转换为测试向量(由于数据格式较为特殊,因此需要先进行转化)
每个txt文件用矩阵来表示一个汉字,读取这个文件,转化为一个1xn的矩阵存储
第二步:分别读取目录中的所有训练文件和测试文件,其实质同上
2、代码实现
(1)准备数据:将图像转换为测试向量
文件格式:
#准备数据:将图像转换为测试向量---img2vector
#将一个32x32的二进制矩阵转化为1x1024的向量,这样上述的分类器就可以处理数字图像信息了
def img2vector(filename):
returnVect = zeros((1,1024))
fr=open(filename)
for i in range(32): #循环读出32行
lineStr=fr.readline()
for j in range(32): #每行的头32个字符值存储在Numpy数组中
returnVect[0,32*i+j] = int(lineStr[j])
return returnVect
(2)使用k-近邻算法的手写识别系统
#使用k-近邻算法的手写识别系统
def handwritingClassTest():
hwLabels = []
trainingFileList = listdir(r'trainingDigits') #将该文件目录中的文件内容存储在列表中
m=len(trainingFileList) #有多少文件
trainingMat = zeros((m, 1024)) #创建一个m行1024列的训练矩阵,矩阵中的每个数据项是1024列,该矩阵的每行数据存储一个图像
for i in range(m):
fileNameStr=trainingFileList[i]
fileStr=fileNameStr.split(".")[0]
classNameStr=int(fileStr.split("_")[0])
hwLabels.append(classNameStr) #该目录下的文件按照规则命名,如文件9_18.txt的分类是9,它是数字9的第18个实例,将类代码存储在hwLabels中
trainingMat[i,:] = img2vector('trainingDigits/%s' % fileNameStr)
#开始测试
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])
vectorUnderText=img2vector('testDigits/%s' % fileNameStr)
classifierResult=classify0(vectorUnderText,trainingMat,hwLabels,3)
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 error rate is: %f" % (errorCount / float(mTest)))