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

【机器学习实战02】k-近邻算法

2016-08-12 14:26 549 查看

1、k-近邻算法概述

k-近邻算法采用测量不同特征值之间的距离方法进行分类。

优点:精度高、对异常值不敏感、无数据输入假定

缺点:计算复杂度高、空间复杂度高

适用数据范围:数值型和标称型

K-近邻算法的具体思想如下:

(1)计算已知类别数据集中的点与当前点之间的距离

(2)按照距离递增次序排序

(3)选取与当前点距离最小的k个点

(4)确定前k个点所在类别的出现频率

(5)返回前k个点中出现频率最高的类别作为当前点的预测分类

2、python代码实现

(1)创建数据

#kNN.py
from numpy import *  #科学计算包NumPy
import operator      #运算符模块

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


数据集和标签:

>>> from pylab import *
>>> scatter(1,1)
<matplotlib.collections.PathCollection object at 0x05492750>
>>> scatter(1,1.1)
<matplotlib.collections.PathCollection object at 0x05492C10>
>>> scatter(0,0)
<matplotlib.collections.PathCollection object at 0x05492FD0>
>>> scatter(0,0.1)
<matplotlib.collections.PathCollection object at 0x055CC330>
>>> plt.text(1.03,1,'A',color='red')
<matplotlib.text.Text object at 0x05492690>
>>> plt.text(1.03,1.1,'A',color='red')
<matplotlib.text.Text object at 0x05184BF0>
>>> plt.text(0.03,0,'B',color='red')
<matplotlib.text.Text object at 0x055CC810>
>>> plt.text(0.03,0.1,'B',color='red')
<matplotlib.text.Text object at 0x055CCA50>
>>> show()


图:



(2)构建kNN分类器

#算法核心
#inX:用于分类的输入向量。即将对其进行分类。
#dataSet:训练样本集
#labels:标签向量
#shape:是array的属性,描述一个多维数组的维度
#tile(inX, (dataSetSize,1)):把inX二维数组化,dataSetSize表示生成数组后的行数,1表示列的倍数。整个这一行代码表示前一个二维数组矩阵的每一个元素减去后一个数组对应的元素值,这样就实现了矩阵之间的减法,简单方便得不让你佩服不行!

#axis=1:参数等于1的时候,表示矩阵中行之间的数的求和,等于0的时候表示列之间数的求和。

#argsort():对一个数组进行非降序排序

#classCount.get(numOflabel,0) + 1:get():该方法是访问字典项的方法,即访问下标键为numOflabel的项,如果没有这一项,那么初始值为0。然后把这一项的值加1。所以Python中实现这样的操作就只需要一行代码,实在是很简洁高效。

def classify0(inX,dataSet,labels,k):
#距离计算:欧氏距离
dataSetSize = dataSet.shape[0]]#得到数组的行数。即知道有几个训练数据
diffMat = tile(inX,(dataSetSize,1)-dataSet)#tile:numpy中的函数。tile将原来的一个数组,扩充成了4个一样的数组。diffMat得到了目标与训练数值之间的差值。
sqDiffMat = diffMat**2#各个元素分别平方
sqDistances = sqDiffMat.sum(axis=1))#对应列相乘,即得到了每一个距离的平方
distances = sqDistances**0.5#开方,得到距离
sortedDistIndicies = distances.argsort()#升序排列
classCount={}
#选择距离最小的k个点
for i in range(k):
voteIlabels = labels[sortedDistIndicies[i]]
classCount[voteIlabels] = classCount.get(voteIlabels,0)+1
#排序
sortedClassCount = sorted(ClassCount.iteritems(),key=operator.itemgetter(1),reverse=True)
return sortedClassCount[0][0]


3、分析

(1)文件目录

Python 2.7.11 (v2.7.11:6d1b6a68f775, Dec  5 2015, 20:32:19) [MSC v.1500 32 bit (Intel)] on win32
Type "copyright", "credits" or "license()" for more information.
>>> import os     #导入os文件
>>> os.getcwd()   #显示当前目录
'C:\\Python27'
>>> os.chdir("G:\MachineLearning\machinelearninginaction\Ch02") #修改目录
>>> os.getcwd()
'G:\\MachineLearning\\machinelearninginaction\\Ch02'
>>>
>>> import os
>>> import kNN  #加载kNN模块
>>> group,labels = kNN.createDataSet()  #创建变量group,labels
>>> group       #查看定义的两个变量正确性
array([[ 1. ,  1.1],
[ 1. ,  1. ],
[ 0. ,  0. ],
[ 0. ,  0.1]])
>>> labels
['A', 'A', 'B', 'B']
>>>


(2)tile函数

>>> #tile(inX,i)   扩展长度
>>> #tile(inX,(i,j))   i是扩展个数,j是扩展长度
>>>
>>> from numpy import *   #导入科学计算包
>>> import operator       #导入运算符模块
>>> inX = array([[0,0],[1,2]])  #定义一个元组
>>> tile(inX,2)
array([[0, 0, 0, 0],
[1, 2, 1, 2]])
>>> tile(inX,1)
array([[0, 0],
[1, 2]])
>>> tile(inX,(1,2))
array([[0, 0, 0, 0],
[1, 2, 1, 2]])
>>> tile(inX,(2,1))
array([[0, 0],
[1, 2],
[0, 0],
[1, 2]])
>>>


4、实施kNN算法

>>> import kNN
>>> kNN.classify0([0,0.5],group,labels,3)
'B'
>>> kNN.classify0([1,1.5],group,labels,3)
'A'
>>> kNN.classify0([0.8,1.2],group,labels,3)
'A'


from pylab import *

#创建数据集
scatter(1,1)
scatter(1,1.1)
scatter(0,0)
scatter(0,0.1)

#创建标签
plt.text(1.03,1,'A',color='red')
plt.text(1.03,1.1,'A',color='red')
plt.text(0.03,0,'B',color='red')
plt.text(0.03,0.1,'B',color='red')

#测试数据
scatter(0,0.5,color='red')
plt.text(0.03,0.5,'(0,0.5)',color='red')

scatter(1,1.5,color='red')
plt.text(1.03,1.5,'(1,1.5)',color='red')

scatter(0.8,1.2,color='red')
plt.text(0.83,1.2,'(0.8,1.2)',color='red')

show()




5、完整实现

将kNN.py和本文件放到同一个目录下,用于导入kNN.py

# coding : utf-8

from numpy import *
import operator
import kNN

group, labels = kNN.createDataSet()

def classify(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0]                    #得到数组的行数,即知道有几个数据集
diffMat = tile(inX, (dataSetSize,1)) - dataSet    #样本与训练集的差值矩阵
sqDiffMat = diffMat**2                            #各元素分别平方
sqDistances = sqDiffMat.sum(axis=1)               #每一行元素求和
distances = sqDistances**0.5                      #开方,得到距离
sortedDistances = distances.argsort()             #按照distances中元素进行升序排列后得到相应下表的列表
classCount = {}                                   #选择距离最小的k个点
for i in range(k):
numOflabel = labels[sortedDistances[i]]
classCount[numOflabel] = classCount.get(numOflabel,0) + 1
sortedClassCount = sorted(classCount.iteritems(), key=operator.itemgetter(1),reverse=True) #按classCount字典的value(类别出现次数)降序排列
return sortedClassCount[0][0]

test = classify([0,0], group, labels, 3)
print test


运算结果为:

B

输出结果是B:说明我们新的数据([0,0])是属于B类。

6、函数

shape函数

是numpy.core.fromnumeric中的函数,它的功能是读取矩阵的长度,比如shape[0]就是读取矩阵第一维度的长度。它的输入参数可以是一个整数表示维度,也可以是一个矩阵。如下所示:

>>> from numpy import *
>>> shape([1])
(1L,)
>>> shape([[1],[2]])
(2L, 1L)
>>> shape(3)
()
>>>
>>> e=eye(3)
>>> e
array([[ 1.,  0.,  0.],
[ 0.,  1.,  0.],
[ 0.,  0.,  1.]])
>>> e.shape
(3L, 3L)
>>> e.shape[0]
3L
>>>


一维矩阵,shape([1]),结果为(1L,);

二维矩阵,shape([1],[2]), 结果为(2L,1L);

单独的数字,返回值为空;

创建一个单位矩阵e,可以判断e的形状,e.shape的结果为(3L,3L); 我们只取第一维度长度(即行数)e.shape[0]的值为3L。

tile函数

tile函数是模板numpy.lib.shape_base中的函数。函数的形式是tile(A,reps)。 A的类型众多,几乎所有类型都可以:array, list, tuple, dict, matrix以及基本数据类型int, string, float以及bool类型。reps的类型也很多,可以是tuple,list, dict, array, int,bool.但不可以是float, string, matrix类型。

tile函数是为了扩充数组元素,看一下举例:

 

 

>>> from numpy import *
>>> a=array([1,2])
>>> tile(a,(3,2))      #构造3X2个copy
array([[1, 2, 1, 2],
[1, 2, 1, 2],
[1, 2, 1, 2]])
>>> tile(7,(3,2))
array([[7, 7],
[7, 7],
[7, 7]])
>>>


简单理解tile(a,(3,2))就是对a进行3X2的复制。具体的理解可以参考这篇文章:http://blog.sina.com.cn/s/blog_6bd0612b0101cr3u.html

在算法程序中tile(inX, (dataSetSize,1))就是对inX进行了复制了dataSetSize行,1表示列的倍数。

diffMat = tile(inX, (dataSetSize,1)) - dataSet 这一行代码表示前一个二维数组矩阵的每一个元素减去后一个数组对应的元素值,实现了矩阵之间的减法。

sqDistances = sqDiffMat.sum(axis=1)这一行代码中,axis=1:参数等于1的时候,表示矩阵中行之间的数的求和,等于0的时候表示列之间数的求和。

classCount.get(numOflabel,0) + 1:这一行代码中使用了字典的get方法,在这里需要理解,get方法可以访问字典中键对应的值。key存在则返回对应value,键不存在返回None,也可以设定自己的返回参数,设置的方法是:变量名.get(键名,参数)。在这里我们设置的参数为0,说明在访问不到键对应值时,返回的参数为0,然后对其加1,如此循环,便可以得到key出现的频率。

sorted函数

为Python内置的排序函数,可以对list或者iterator进行排序,官网文档见:http://docs.python.org/2/library/functions.html?highlight=sorted#sorted,该函数原型为:

sorted(iterable[, cmp[, key[, reverse]]])

参数解释:

(1)iterable指定要排序的list或者iterable;

(2)cmp为函数,指定排序时进行比较的函数,可以指定一个函数或者lambda函数,如:

students为类对象的list,每个成员有三个域,用sorted进行比较时可以自己定cmp函数,例如这里要通过比较第三个数据成员来排序,代码可以这样写:

students = [(‘john’, ‘A’, 15), (‘jane’, ‘B’, 12), (‘dave’, ‘B’, 10)]

sorted(students, key=lambda student : student[2])

(3)key为函数,指定取待排序元素的哪一项进行排序,函数用上面的例子来说明,代码如下:

sorted(students, key=lambda student : student[2])

key指定的lambda函数功能是去元素student的第三个域(即:student[2]),因此sorted排序时,会以students所有元素的第三个域来进行排序。

  也可以用operator.itemgetter函数来实现,例如要通过student的第三个域排序,可以这么写:

  sorted(students, key=operator.itemgetter(2))

  sorted函数也可以进行多级排序,例如要根据第二个域和第三个域进行排序,可以这么写:

  sorted(students, key=operator.itemgetter(1,2)) 即先跟句第二个域排序,再根据第三个域排序。

(4)reverse参数就不用多说了,是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时将按降序排列。

在算法程序中,我们也可以用另一种方法来获得最大概率的分类,代码修改如下:

classCount = {}
for i in range(k):
numOflabel = labels[sortedDistances[i]]
classCount[numOflabel] = classCount.get(numOflabel,0) + 1

maxCount = 0
for key, value in classCount.items():
if value > maxCount:
maxCount = value
maxIndex = key
return maxIndex
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  机器学习 python