k-近邻算法在约会网站上的使用

"""
(1) 收集数据:提供文本文件
(2) 准备数据:使用Python解析文本文件
(3) 分析数据:使用Matplotlib画二维扩散图
(4) 训练算法:此步骤不适用k-近邻算法
(5) 测试算法:使用海伦提供的部分数据作为测试样本
 测试样本和非测试样本的区别在于:测试样本是已经完成分类的数据,如果预测分类与实际样本类别不同
 则标记为一个错误
(6) 使用算法:产生简单的命令行程序,然后海伦可以输入一些特征数据以判断对方
是否为自己喜欢的类型
"""
import matplotlib.pyplot as plt
from numpy import *


def classify0(inX, dataSet, labels, k):
    """
    共有四个输入参数:用于分类的输入向量是inX,输入的训练样本集为dataSet
    标签向量为labels, 最后的参数k表示用于选择最邻近的数目,其中标签向量的元素数目和
    矩阵dataSet的行数相同。计算的距离为欧氏距离
    :param inX:
    :param dataSet:
    :param labels:
    :param k:
    :return:
    """
    # 计算欧氏距离
    dataSetSize = dataSet.shape[0]  # dataSet.shape[0]输出的是数据集的行数,shape[1]输出的是数据集的列数
    diffMat = tile(inX, (dataSetSize, 1)) - dataSet  # tile函数共有两个参数,tile(A, reps), A指待输入数组,reps决定A重复的次数,整个函数用于重复数组A来构建新的数组
    sqDiffMat = diffMat ** 2  # 计算欧氏距离
    sqDistances = sqDiffMat.sum(axis=1)  # 按行求和,
    distance = sqDistances ** 0.5  # 开根号,结果为该未知数据集到每一个已知数据集的欧氏距离

    # 选择距离最小的k个点
    sortedDistIndicies = distance.argsort()  # argsort()方法返回数组值从小到大的索引
    classCount = {}

    for i in range(k):
        voteIlabel = labels[sortedDistIndicies[i]]  # 获取所属类别
        classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1  # get方法返回指定键的值,如果不存在则返回默认值(0:设置默认值为0)
    sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)  # items()返回字典列表操作后的迭代  sorted(可迭代对象, 自定义的比较函数, 顺序默认为Fslse,正序)
    return sortedClassCount[0][0]


def file2matrix(filename):
    """
    该函数输入为文件名字符转, 输出为训练样本矩阵和类标签向量
    :param filename:
    :return:
    """
    fr = open(filename)
    arrayLines = fr.readlines()  # readlines()方法读取整个文件所有行,保存在一个列表变量中,每行作为一个元素,但读取大文件时比较占内存
    numberOfLines = len(arrayLines)  # 输出列表的长度,即文件的长度
    print("文件的长度为:", numberOfLines)

    returnMat = zeros((numberOfLines, 3))  # 创建返回的NumPy矩阵zeros方法返回来一个给定形状和类型的用0填充的数组
    classLabelVector = []
    index = 0
    for line in arrayLines:
        line = line.strip()  # strip方法可饭会移除字符串头尾指定的字符串生成新的字符串(传入指定参数)截取所有的回车符

        # print(line)
        # print("-"*100)
        listFromLine = line.split("\t")
        # print(listFromLine)
        # print("-" * 100)

        returnMat[index, :] = listFromLine[0: 3]
        classLabelVector.append(int(listFromLine[-1]))
        index += 1
    return returnMat, classLabelVector


def myplot(returnMat):
    """
    绘图
    :param returnMat:
    :return:
    """
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(returnMat[:, 1], returnMat[:, 2])
    plt.show()


def autoNorm(dataSet):
    """
    归一化处理,每一列的最小值放在变量minVals中,将最大值放在变量maxVals中,其中
    dataSet.min(0)中的参数0使得函数可以从列中选取最小值,而不是选取当前行的最小值
    然后,函数计算可能的取值范围,并创建新的返回矩阵,为了归一化特征值,我们必须使用当前值
    减去最小值,然后除以取值范围,需要注意的是,特征值矩阵有1000*3个值,而minVals和ranges的
    都为1*3,为了解决这个问题,我们使用NumPy库中tile()函数将变量内容复制成输入矩阵同样大小的矩阵
    的矩阵,
    :param dataSet:
    :return:
    """
    minVals = dataSet.min(0)  # min(0)返回该矩阵中每一列的最小值,min(1)返回该矩阵中每一行的最小值
    maxVals = dataSet.max(0)  # max(0)返回该矩阵中每一列的最大值,max(1)返回该矩阵中每一行的最大值
    ranges = maxVals - minVals
    zeros(shape(dataSet))  # shape(dataSet)返回dataSet矩阵的类型(1000×3), zeros返回零矩阵
    m = dataSet.shape[0]  # 返回数据的行值
    normDataSet = dataSet - tile(minVals, (m, 1))
    normDataSet = normDataSet / tile(ranges, (m, 1))
    return normDataSet, ranges, minVals


def datingClassTest():
    """
    首先使用file2matrix和autoNorm函数从文件中读取数据并将其转换为归一化特征值
    接着计算测试向量的数量,此步决定了哪些数据用于测试,哪些数据用于分类器的训练样本
    然后将这两部分数据输入到原始分类器函数classify0,最后,函数计算错误率并输出结果
    此外我们使用原始分类器
    :return:
    """
    hoRatio = 0.10
    # 读取数据,转为矩阵形式
    datingDataMat, datingLabels  = file2matrix("data/datingTestSet2.txt")

    # 将数据转换为归一化特征值
    normMat, ranges, minVals = autoNorm(datingDataMat)
    m = normMat.shape[0]
    numTestVecs = int(m * hoRatio)  # 选取90%作为训练集,10%作为测试集
    errorCount = 0.0

    for i in range(numTestVecs):

        classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :], datingLabels[numTestVecs:m], 3)
        print("the classifierResult 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)))


if __name__ == '__main__':
    datingClassTest()



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值