简而言之K_邻近算法是采用测量不同特征值之间距离的方法进行分类
优点:精度高、对异常值不敏感、无数据输入假定
缺点:计算复杂度高、空间复杂度高
适用数据范围:数值型和标称型
我们先导入一些要用到的库
import matplotlib.pyplot as plt
from numpy import *
import operator
#在代码前加上这些,之后的在图形注释上就可以显示中文了,不然会出现乱码
from pylab import mpl
mpl.rcParams['font.sans-serif']=['SimHei']
#声明好一组简单的数据点和类别标签 用于测试算法
group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels=['A','A','B','B']
上面这组简单的数据可以看成 每个特征就是坐标(x,y)类别要么属于A 要么属于B 如果我们需要预测一个位置类别的数据点,利用k_邻近算法的思路,这里可以计算未知类别的坐标点到各个已知类别的坐标点距离之后我们将Di从小到大排序,取前K个已知坐标点的类别,这k个中出现频率最高的类别就是位置类别的坐标点的类别。这也是K邻近算法名字的由来。
下面通过代码实现K_临近算法,并用于改进约会网站的配对效果和手写字体识别两个项目进行测试
def classify0(inX,dataSet,labels,k):
# 计算目标到各样本距离
dataSetsize=dataSet.shape[0]#得到数据集的行数
diffMat=tile(inX,(dataSetsize,1))-dataSet#通过tile函数生成和数据行数相同的待分类的各项特征,从而对训练集内所有已知类别的数据特征进行做差
sqDiffMat=diffMat**2#做差平方
sqDistances=sqDiffMat.sum(axis=1)#按行求和
distances=sqDistances**0.5#开根号
sortedDistIndicies=distances.argsort()# 排序
#计算各样本标签出现频率
classCount={}#用于存放前k个特征类别
for i in range(k):
voteIlabel=labels[sortedDistIndicies[i]]
classCount[voteIlabel]=classCount.get(voteIlabel,0)+1#此for循环用于统计各类别在前k个中出现的次数
sortedClassCount=sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)#sorted函数中三个参数,第一个为比较对象,第二个为比较对象中的某一属性,reverse为bool型01对应升序和降序
#items()迭代器遍历字典 operator.itemgetter()选择比较的属性
return sortedClassCount[0][0]
可见,K_邻近算法的实现很简单,思路也十分简单。
实例一先用于改进约会网站的配对效果实践
海伦收集了1000条约会数据每个人主要包含以下三种特征:
每年获得的飞行常客里程数
玩视频游戏所耗时间百分比
每周消费的冰淇淋公升数
通过这三个特征相应数据 他将男性氛围1,2,3 三个等级
我们先大体观察一下数据样式
数据十分规整,每列含义也很清晰,下面我们先写一个读入数据的函数并将其存为Numpy的解析程序,以便于应用k_临近算法
def file2matrix(filename):
fr = open(filename)
numberOfLines = len(fr.readlines()) #得到数据集行数
returnMat = zeros((numberOfLines,3)) #准备一个数据矩阵用于存放读取数据集特征便于计算
classLabelVector = [] #标签列表
index = 0#用于写入returnMat过程中换行
for line in fr.readlines():
line = line.strip()
listFromLine = line.split('\t')
returnMat[index,:] = listFromLine[0:3]
classLabelVector.append(int(listFromLine[-1]))
index += 1
return returnMat,classLabelVector
datingMat,datingLabel=file2matrix('datingTestSet2.txt')#由此我们得到可用于计算的数据集
得到如下形式
分别是包含特征的矩阵和标签列
在实际数据挖掘中,我们可以通过图来更直观的反映数据特征之间与类别标签的关系,这里做一简单展示
#数据可视化
fig=plt.figure()
ax=fig.add_subplot(111)
ax.scatter(datingMat[:,1],datingMat[:,0],15.0*array(datingLabel),15.0*array(datingLabel))
plt.xlabel("飞行公里数")
ax.legend('x')
plt.show()
效果如下
可以直观看出这些不同类别的点分布具有一定的聚集性
再代入算法前,容易发现不同特征数据之间的差别很大,这样直接带入算法,数值大的特征在计算中起到的作用会远远高于其他特征,这在K_临近算法中会大大弱化掉数值笑的特征,所以在带入前我们对不同特征数据进行归一化。归一化函数如下:
#数据归一化
def autoNorm(dataset):
minval=dataset.min(0)
maxval=dataset.max(0)
ranges=maxval-minval
newdata=zeros(shape(dataset))
m=dataset.shape[0]
newdata=dataset-tile(minval,(m,1))
newdata=newdata/tile(ranges,(m,1))
return newdata
此时将归一化后的数据集带入算法进行分类,我们选取1000条数据中的100条数据作为测试集,900条数据作为训练集,代码如下
def datingClassTest():
errorcount=0.0
newdata=autoNorm(datingMat)
m=newdata.shape[0]
ceshi=0.1
ceshidata=int(m*ceshi)
for i in range(ceshidata):
classdataresult=classify0(newdata[i,:],newdata[ceshidata:m,:],datingLabel[ceshidata:m],3)
print("The predict label is %d,the real label is %d"%(classdataresult,datingLabel[i]))
if(classdataresult!=datingLabel[i]): errorcount=errorcount+1.0
zhunquelv=errorcount/float(ceshidata)
print("预测错误率:%f"%(zhunquelv))
代码运行部分结果如下
可见预测的错误率为0.05,分类效果还算满意!
实例2.利用K_临近算法进行手写字体识别
我们这里识别的数字为0,1,2,3,4,5,6,7,8,9。每个数字由一个32x32的二进制图像矩阵表示。
第一步我们需要将32x32的图像矩阵转化为一个1x1024的向量,从而才能带入k_邻近算法进行计算。
转化函数如下
#将32x32的二进制图像转为1x1024的向量
def img2vector(filename):
dataVector=zeros((1,1024))#用于保存生成的1x1024的向量
fr=open(filename)
for i in range(32):
cengshu=fr.readline()
for j in range(32):
dataVector[0,32*i+j]=int(cengshu[j])
return dataVector
数据集形式如下
此时我们可以发现每个类别标签保存在文件的命名里,如0_180即第180个示数为0的32x32矩阵。此时我们需要引入一个新的库,在代码开头加上
from os import listdir
用listdir从文件名中解析出标签
def handwritingClassTest():
hwLabels=[]#用于存放标签
trainingFileList=listdir('trainingDigits')#读取该目录下所有文件名
m=len(trainingFileList)#计算文件个数,即数据集中样本数目
trainingMat=zeros((m,1024))#用于保存将所有图片转化为1x1024向量的矩阵
for i in range(m):
firstfenge=trainingFileList[i].split('.')[0]
secondfenge=int(firstfenge.split('_')[0])#从文件名中解析出标签
hwLabels.append(secondfenge)
trainingMat[i,:]=img2vector('trainingDigits/%s'%trainingFileList[i])
testingFileList=listdir('testDigits')
errorcount=0.0
testm=len(testingFileList)
#下面将训练集和测试集分别输如之前写好的classify0分类函数
for i in range(testm):
Filename=testingFileList[i]
testfirstfenge=Filename.split('.')[0]
reallabel=int(testfirstfenge.split('_')[0])
testingMat=img2vector('testDigits/%s'%Filename)
predictresult=classify0(testingMat,trainingMat,hwLabels,3)
print("the real label is %d,the predict label is %d"%(reallabel,predictresult))
if(predictresult!= reallabel):errorcount=errorcount+1.0
print("错误率:%f"%(errorcount/testm))
程序部分运行结果如下
可见效果还不错,错误率为0.010571
以上便是k_临近算法的基本写法和一些简单的实例应用。