本篇文章主要内容:
- k-临近分类算法概述
- 从文本文件中解析和导入数据
- 使用Matplotlib创建扩散图
- 归一化数据
一、k-临近算法概述
简单地说,k-临近算法采用测量不同特征值之间的距离方法进行分类
工作原理:
存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似(最临近)的数据的分类标签。一般来说,我们只选择样本数据集中前k个最相似的数据,这就是k-临近算法的出处,通常k是不大于20的整数。最后,选择k个最相似数据中出现次数最多的分类,作为新数据的分类。
我们使用欧式距离公式,来计算两个向量点A(x1,y1)与B(x2,y2)之间的距离:
优缺点:
优点:精度高、对异常值不敏感
缺点:计算复杂度高、空间复杂度高
适用数据范围:数值型和标称型
k-临近算法核心内容伪代码如下:
对未知类别属性的数据集中的每个点依次执行以下操作:
1.计算已知类别数据集中的点与当前点之间的距离
2.按照距离递增次序排序
3.选取与当前点距离最小的k个点
4.确定前k个点所在类别的出现频率
5.返回前k个点出现频率最高的类别作为当前点的预测分类
二、归一化数据
在分析数据的过程中,我们很容易发现,因为不同特征的量纲(单位)的不同,不同特征的数据有很大差值,这样对计算结果的影响很大,显然,在运用欧氏距离公式时差值最大的属性对计算结果影响最大。在处理这种不同不同取值范围的特征值时,我们通常采用的方法是将数值归一化,下面的公式可以将任意取值范围的特征值映射到0到1区间内:
示例:在约会网站上使用k-临近算法
步骤:
- 收集数据:提供文本文件
- 准备数据:使用python解析文本文件
- 分析数据:使用matplotlib画二维扩散图
- 设计算法:运用欧式距离公式设计k-临近算法
- 测试算法:如果预测结果与实际不同,则标记为一个错误
- 使用算法:产生简单的命令行程序,可以输入一些特征数据以判断对方是否是自己喜欢的类型
具体代码运行如下:
导入numpy库
import numpy as np
读取数据
def filematrix(filename): #将待处理数据改变为可以接受的格式
fr=open(filename) #打开文件
numberOfLines=len(fr.readlines()) #得到文件的行数 readlines()函数,逐行读取整个文件
returnMat=np.zeros((numberOfLines,3)) #返回的Numpy零矩阵,numberOfLines行,3列
#return=np.array(returnMat)
fr=open(filename)
classLabelVector=[] #创建返回的numpy矩阵
index=0 #计数
for line in fr.readlines(): #依次读取文件的第一行
line=line.strip() #s.strip(rm),当rm空时,默认删除空白符(包括‘\n’,'\r','\t','')
listFromLine=line.split('\t') #使用s.split(str=“”,num=string,cout(str))将字符串根据‘\t’分隔符进行切片
returnMat[index,:]=listFromLine[0:3] #将数据前三列提取出来,存放到returnMat的Numpy矩阵中,也就是特征矩阵
classLabelVector.append(int(listFromLine[-1]))#根据文本中标记的喜欢程度进行分类,1代表不喜欢,2代表魅力一般,3代表及具魅力
index=index+1
return returnMat,classLabelVector
分析数据
import matplotlib
import matplotlib.pyplot as plt
fig=plt.figure() #建立一个画布
ax=fig.add_subplot(111) #在画布中建立图表,fig.add_subplot()函数。画布分割成1行1列,图像
ax.scatter(datingDataMat[:,1],datingDataMat[:,2],10*np.array(datingLabels),10*np.array(datingLabels)) ##第二列和第三列数据,
plt.show()
输出分析结果图(不同的颜色代表不同的特征)
归一化数据
def autoNorm(dataSet):
minVals=dataSet.min(0) #每一列的最小值
maxVals=dataSet.max(0) #每一列的最大值
ranges=maxVals-minVals
normDataSet=np.zeros(np.shape(dataSet)) #shape(dataSet)返回dataSet
m=dataSet.shape[0] #返回dataSet的行数
normDataSet=dataSet-np.tile(minVals,(m,1)) #原dataSet数据矩阵最小值
normDataSet=normDataSet/np.tile(ranges,(m,1))#矩阵除法
return normDataSet,ranges,minVals
设计k-临近算法
def classify(inX,dataSet,labels,k):
dataSetSize=dataSet.shape[0]
diffMat=np.tile(inX,(dataSetSize,1))-dataSet
sqDiffMat=diffMat**2
sqDistances=sqDiffMat.sum(axis=1)
distances=sqDistances**0.5
sortedDistIndicies=distances.argsort()
classCount={ }
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]
测试算法
import operator
def datingClassTest(): #定义函数,进行分类测试
hoRatio=0.10 #10%作为测试集
datingDataMat,datingLabels=filematrix('datingTestSet2.txt') #打开数据,
normMat,ranges,minVals=autoNorm(datingDataMat) #调用函数进行数据归一化
m=normMat.shape[0] #获得行数,即总数量
numTestVecs=int(m*hoRatio) #取10%测试数据的个数,变成int类型
errorCount=0.0 #错误计数
for i in range(numTestVecs):
classifierResult=classify(normMat[i,:],normMat[numTestVecs:m,:],datingLabels[numTestVecs:m],3)
#取出前10%行作为测试集,后面的作为训练集,标签与数据范围相同,K=3
print("the classifier came back with:%d,the real answer is:%d"%(classifierResult,datingLabels[i]))
#classifierResult分类测试计算出的值,datingLabels[i]真实值
if(classifierResult!=datingLabels[i]):
errorCount+=1.0 #预测值与真实值不同时,错误数+1
print("the total error rate is:%f"%(errorCount/float(numTestVecs)))
print(errorCount)
datingClassTest()
最终输出的错误率与错误个数:
使用算法
def classifyPerson():
resultList=['一点也不受欢迎','比较受欢迎','极具魅力']
ffMiles=float(input("每年飞行里程大约有多少?"))
percentTats=float(input("玩游戏的时间百分比是多少?"))
iceCream=float(input("每年会消耗点多少公升的冰淇淋?"))
datingDataMat,datingLabels=filematrix('datingTestSet2.txt')
normMat,ranges,minVals=autoNorm(datingDataMat)
inArr=np.array([ffMiles,percentTats,iceCream])
classifierResult=classify((inArr-minVals)/ranges,normMat,datingLabels,3)
print("你可能是这种人:",resultList[classifierResult-1])
classifyPerson()
输出结果: