kNN分类算法及案例


1、K-近邻算法概述

邻近算法,或者说K最近邻(kNN,k-NearestNeighbor)分类算法是数据挖掘分类技术中最简单的方法之一。所谓K最近邻,就是k个最近的邻居的意思,说的是每个样本都可以用它最接近的k个邻居来代表,有种“近朱者赤,近墨者黑”的思想。

它的工作原理是:存在一个样本数据集合,也称作训练样本集,并且样本集中每个数据都存在标签,即我们知道样本集中的每一个数据与所属分类的对应关系。输入没有标签的新数据后,将新数据的每个特征与样本集中数据对应的特征进行比较,然后算法提取样本集中特征最相似数据(最近邻)的分类标签。一般来说,我们只选择样本数据集中前K个最相似的数据,这就是K-近邻算法K的出处,通常K是不大于20的整数。最后选择K个最相似的数据中出现次数最多的分类,作为新数据的分类。

如下图所示,如何判断绿色圆应该属于哪一类,是属于红色三角形还是属于蓝色四方形?如果K=3,由于红色三角形所占比例为2/3,绿色圆将被判定为属于红色三角形那个类,如果K=5,由于蓝色四方形比例为3/5,因此绿色圆将被判定为属于蓝色四方形类。
Alt

def createDataSet():           #返回一个数据集
    group = np.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):
    dataSetSize = dataSet.shape[0]       #数据集的行数
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
    #tile是numpy中的函数,inX可以是数值或者数组,tile(A,(a,b))即将A行重复a次,列重复b次,得到一个array型数组
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis = 1)     #参数axis=1表示将对列表的每一行求和
    distances = sqDistances**0.5 
    sortedDistIndicies = distances.argsort()  #argsort()是numpy中的方法,是返回从小到大排序后值的索引值顺序
    classCount = {}                           #创建一个空字典
    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]      #获取前K个的标签
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1    
        #字典中的get方法查询字典中是否包含voteIlabel这个key值,若无则创建该key值并初始化value值为0,若有就直接获取该值,不需创建
        sortedClassCount = sorted(classCount.items(), key = operator.itemgetter(1), reverse = True)
        #classCount.items()是将字典拆分为元组所组成的列表, itemgetter(1)是表示按索引为1列的值进行排序,True表示逆序由大到小
    return sortedClassCount[0][0]

简单的说,K-近邻算法采用测量不同特征值之间的距离方法来进行分类。
优点:精度高、对异常值不敏感、无数据输入假定。
缺点:计算复杂度高、空间复杂度高。
适用数据范围:数值型和标称型

2、示例:使用K-近邻算法改进约会网站的配对效果

一般来说,约会网站会推荐不同类型的人选,一般按自己的喜欢程度可以分为:不喜欢的人、魅力一般的人和极具魅力的人这三种类型。人们倾向于在周一到周五和那些魅力一般的人约会,在周末和极具魅力的人约会。我们可以通过K-近邻算法将匹配对象进行分类,从而更好地选择约会对象。
K-近邻算法的使用步骤一般如下:
(1)收集数据
(2)准备数据(数据预处理)
(3)分析数据:可以使用Matplotlib画二维扩散图
(4)测试算法
(5)使用算法
:K-近邻算法不需要训练算法!!!!

2.1准备数据:从文本中解析数据

数据存放在datingTestSet.txt文件中,每个样本数据占一行,总共有1000行。数据包含以下3种特征:

  • 每年获得的飞行常客里程数
  • 玩视频游戏所耗时间百分比
  • 每周消费的冰淇淋公升数

在这里插入图片描述
在将特征数据输入到分类器之前,必须将待处理数据的格式改变为分类器可以接受的格式。下面创建一个file2matrix函数,用来处理格式问题。

####将文本记录转换为NumPy的解析程序

def file2matrix(filename):
    fr = open(filename)
    arrayOLines = fr.readlines()        #生成一个列表,每行是列表的一个元素,每个元素里面包含了空格和换行符
    numberOfLines = len(arrayOLines)    
    returnMat = np.zeros((numberOfLines, 3))  
    classLabelVector = []               #用于存储标签的,即列表的最后一列
    index = 0
    for line in arrayOLines:
        line = line.strip()             #去掉每一行的回车字符
        listFromLine = line.split('\t')     #将每一行按照空格分割
        returnMat[index, :] = listFromLine[0: 3]     #将数据存入returnMat和classLabelVector矩阵中
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector

2.2分析数据:使用Matplotlib创建散点图

散点图使用datingDataMat矩阵的第二、三列数据,分别表示特征值“玩游戏所耗时间百分比”和“每周所消耗的冰淇淋公升数”

fig = plt.figure()
ax = fig.add_subplot(111)
ax.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0*np.array(datingLabels), 15.0*np.array(datingLabels))
plt.show()

2.3准备数据:归一化数值

( 0 − 67 ) 2 + ( 20000 − 32000 ) 2 + ( 1.1 − 0.1 ) 2 \sqrt[]{(0-67)^2 + (20000-32000)^2+(1.1-0.1)^2} 0672+20000320002+1.10.12
当计算距离的时候,从上面方程中可以看出数字差值最大的属性对计算结果的影响最大,产生这种现象的原因是由于某个特征值远大于其他特征值,它们不属于同一个数量级,但是由于假设各种特征等权重,因此在处理这种不同取值范围的特征值的时候,通常选择将数值归一化,如下面这个公式可以将特征值转换到0和1之间:
newValue = (oldValue - min)/ (max - min)
其中min和max是数据集中最小特征值和最大特征值

###归一化特征值####

def autoNorm(dataSet):            # 归一化 
    minVals = dataSet.min(0)      # 参数为0即从列中获取最小值
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    normDataSet = np.zeros(np.shape(dataSet))
    m = dataSet.shape[0]
    normDataSet = dataSet - np.tile(minVals, (m, 1))
    normDataSet = normDataSet / np.tile(ranges, (m, 1))
    return normDataSet, ranges, minVals

2.4测试算法:作为完整程序验证分类器

我们通常将已有数据的90%作为训练样本来训练分类器,而将10%的数据用于测试分类器,检测分类器的正确率。

####分类器针对约会网站的测试代码####

def datingClassTest():
    hoRatio = 0.1
    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)))

2.5使用算法:构建完整可用系统

上面对分类器进行了测试,现在使用这个分类器对人们进行分类

####约会网站测试函数####

def classifyPerson():
    resultList = ['not at all', 'in small doses', 'in large doses']
    percentTats = float(input("percentage of time spent playing video games?"))
    ffMiles = float(input("frequent flier miles earned per year?"))
    iceCream = float(input("liters of ice cream consumed per year?"))
    datingDataMat, datingLabels = file2matrix('datingTestSet2.txt')
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = np.array([ffMiles, percentTats, iceCream])
    classifierResult = classify0((inArr - minVals)/ranges, normMat, datingLabels, 3)
    print("You will probably like this person:", resultList[classifierResult - 1])
  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值