Python3《机器学习实战》01:k-近邻算法(完整代码及注释)

运行平台: Windows
Python版本: Python3
IDE: Anaconda3

# -*- coding: utf-8 -*-
"""
Created on Sun Apr 29 20:32:03 2018

@author: WangDan

使用k-近邻算法改进约会网站的配对效果
"""

import numpy as np
import operator
import matplotlib.pyplot as plt

#准备数据1:从文本文件中解析数据
def file2matrix(filename):
    #打开文件
    fr = open(filename)
    #读取文件所有内容,获得行数
    arrayOLines = fr.readlines()
    numberOfLines = len(arrayOLines)
    #准备一个本函数最终要返回的数据解析完成的矩阵,numberOfLines行3列
    returnMat = np.zeros((numberOfLines,3))
    #准备一个本函数最终要返回的分类标签列表
    classLabelVector = [] 
    #行的索引值初始化为0
    index = 0

    for line in arrayOLines:
        #s.strip([chars]),当[chars]为空时,默认删除空白符(包括'\n','\r','\t',' ')
        #否则移除字符串头尾指定的字符
        line = line.strip()
        #使用str.split(str="", num=string.count(str))将字符串进行切片
        #str -- 分隔符,默认为所有的空字符,包括空格、换行(\n)、制表符(\t)等
        #num -- 分割次数
        listFromLine = line.split('\t')
        #将第line行分割出来的列表前3列存入特征矩阵returnMat
        returnMat[index,:] = listFromLine[0:3]
        #将txt文件中分类标签转化为整数形式,1为不喜欢,2为魅力一般,3为极具魅力
        if listFromLine[-1] == 'didntLike':
            classLabelVector.append(1)
        elif listFromLine[-1] == 'smallDoses':
            classLabelVector.append(2)
        elif listFromLine[-1] == 'largeDoses':
            classLabelVector.append(3)
        index += 1
    return returnMat,classLabelVector

#数据可视化,绘制散点图
def showdatas(datingDataMat, datingLabels):
    #创建画布,设置大小
    fig = plt.figure(figsize=(8,5))
    ax = fig.add_subplot(311)
    #spyder默认使用自带的字体,无法使用系统的其他字体,而自带字体库中缺省中文字体
    #加入下面两行语句可以解决散点图中不能输出中文的问题
    plt.rcParams["font.sans-serif"] = ["Microsoft YaHei"]  
    plt.rcParams['axes.unicode_minus'] = False 
    #u表示unicode string,表示使用unicode进行编码,没有u表示byte string,类型是str
#    ax.set_title(u'每年获得的飞行常客里程数与玩视频游戏所消耗时间占比')  
    ax.scatter(datingDataMat[:,0], datingDataMat[:,1], 
               3.0*np.array(datingLabels), 3.0*np.array(datingLabels))

    bx = fig.add_subplot(312)
    bx.scatter(datingDataMat[:,0], datingDataMat[:,2], 
               3.0*np.array(datingLabels), 3.0*np.array(datingLabels))

    cx = fig.add_subplot(313)
    cx.scatter(datingDataMat[:,1], datingDataMat[:,2], 
               3.0*np.array(datingLabels), 3.0*np.array(datingLabels))

#准备数据2:归一化特征值
#有些数据数值大,有些数值小,导致在求欧式距离时,数值大的数据对结果影响大
#为了使所有特征同等重要,所以要对数据进行归一化处理
def autoNorm(dataSet):
    #min(0)表示取出每一列的最小值
    #min(1)表示取出每一行的最小值
    #minVals和maxVals均为1*3的一维数组
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    #shape(np.array([[2,2,2],[3,3,3]]))-->(2, 3)
    #zeros(5)-->array([0., 0., 0., 0., 0.])
    #zeros([2,3])-->array([[0., 0., 0.], [0., 0., 0.]]),注意使用列表作为参数
    normDataSet = np.zeros(np.shape(dataSet))
    #当shape=0为数组行数,shape=1为数组列数
    m = dataSet.shape[0]
    #矩阵数据操作:原始数据减去最小值的差除以最大值与最小值之间的差,得到归一化结果
    #tile([1,2], (3,1))-->array([[1, 2], [1, 2], [1, 2]]),形成3*2的矩阵
    normDataSet = dataSet - np.tile(minVals, (m,1))#m*3的矩阵
    #矩阵每个数对应相除
    normDataSet = normDataSet/np.tile(ranges, (m,1))
    return normDataSet, ranges, minVals

#k-近邻算法实施
def classify0(inX, dataSet, labels, k):
    #shape[0]为数组行数,shape[1]为数组列数
    dataSetSize = dataSet.shape[0] 
    #inX点与dataSet每个特征值对应相减
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet 
    sqdiffMat = diffMat ** 2
    #如c=np.array([[0,2,1], [3,5,6], [0,1,1]])
    #c.sum()-->19所有元素相加
    #c.sum(axis=0)-->array([3, 8, 8])每一列相加
    #c.sum(axis=1)-->array([ 3, 14,  2])每一行相加
    sqDistances = sqdiffMat.sum(axis=1) 
    #开方后得到inX到dataSet每个点之间的欧氏距离
    distances = sqDistances ** 0.5
    #返回数组值从小到大的索引值,注意是索引值
    sortedDistIndicies = distances.argsort() 
    #定义一个字典,用来存放类别次数
    classCount = {}
    for i in range(k):
        #取出欧氏距离最小的前k个值的标签
        voteIlabel = labels[sortedDistIndicies[i]]
        #dict.get(key,default=None),字典的get()方法,返回指定键voteIlabel的值,
        #如果值不在字典中返回默认值0,键为voteIlabel
        #从而计算出每个标签在前k个值中出现的次数
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
    #python3中用items()替换python2中的iteritems()
    #key=operator.itemgetter(1)根据字典的值进行排序
    #key=operator.itemgetter(0)根据字典的键进行排序
    #reverse=True降序排序字典
    sortedClassCount = sorted(classCount.items(), 
                              key = operator.itemgetter(1), reverse=True)
    #返回出现次数最多的标签,也就是inX最大可能对应的标签
    return sortedClassCount[0][0]

#测试算法
def datingClassTest(datingLabels, normMat):
    #通常随机选择提供数据的10%作为测试数据,另外90%作为训练样本
    hoRatio = 0.10
    #shape[0]为数组行数,shape[1]为数组列数
    m = normMat.shape[0]
    numTestVecs = int(m*hoRatio)
    errorCount = 0.0
    #k-近邻算法实施
    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('\n错误率为: %f\n' % (errorCount/float(numTestVecs)))
    print('错误个数为:%d, 总共样本数为:%d' % (errorCount, numTestVecs))

def classfyPerson(datingLabels):
    resultList = ['不喜欢', '有点喜欢', '非常喜欢']
    #输入各项数据
    percentTats = float(input('玩视频游戏所耗时间百分比:'))
    ffMiles = float(input('每年获得的飞行常客里程数:'))
    iceCream = float(input('每周消费的冰激淋公升数:'))
    normMat, ranges, minVals = autoNorm(datingDataMat)
    inArr = np.array([ffMiles, percentTats, iceCream])
    normInArr = (inArr - minVals) / ranges
    classifierResult = classify0(normInArr, normDataSet, datingLabels, 3)
    print('\n你可能%sTa!' % resultList[classifierResult-1])

if __name__ == '__main__':
    #准备数据:从文本文件中解析数据
    datingDataMat, datingLabels = file2matrix('datingTestSet.txt')
    #分析数据:使用Matplotlib创建散点图
    showdatas(datingDataMat, datingLabels)
    #准备数据:归一化特征值
    normDataSet, ranges, minVals = autoNorm(datingDataMat)
    #测试算法:
    datingClassTest(datingLabels, normDataSet)
    #使用算法
    classfyPerson(datingLabels) 

部分运行结果截图:
这里写图片描述
这里写图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

汪蛋

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值