欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

kNN算法实践2

程序员文章站 2022-07-14 20:32:31
...

引言

我的朋友小美最近一直使用约会网站寻找适合自己的约会对象,由于推选的人数过多,小美想做一个排除,剔除那些她不喜欢的人和魅力一般的人,只和极具魅力的人约会。
好了,作为一个标准的好男儿,是时候为小美做一点贡献了!

回顾我们的步骤:

1.收集数据:小美为我们提供了文本文件
2.准备数据:使用Python解析文本文件,就是导入数据
3.分析数据:画散点图啦,比较想越她的那么多
4.训练算法:kNN那么笨的算法不需要训练;
5.测试算法:使用小美给我们提供的数据进行测试,计算错误率;
6.使用算法:让小美输入一些特征,判断出他是不是小美想约的对象

通过这个案例,你可以学会

1.kNN具体用法
2.数据归一化的重要性

1.收集导入数据

#-*- coding: utf-8 -*-
import numpy as np
import matplotlib.font_manager
import matplotlib.pyplot as plt
import operator
myfont = matplotlib.font_manager.FontProperties(fname="Light.ttc")
# 2.准备数据:小美把他需要的数据放在文本文件datingTestSet2.txt中,一共有1000行的样本
# 样本有三个属性:
(1)每年获得的飞机常客里程数,
(2)玩游戏所消耗的时间,
(3)每周消耗的冰淇淋数
# 好了,开始我们的算法吧!!

def file2matrix(filename):
    fr = open(filename)
    # readlines()方法用于读取所有行(直到结束符EOF)并返回列表,
    arrayOLines = fr.readlines()
    # 得到文件的行数
    numberOfLines = len(arrayOLines)
    returnMat = np.zeros((numberOfLines,3))
    classLabelVector = []
    index =0
    for line in arrayOLines:
        #strip() 方法用于移除字符串头尾指定的字符(默认为空格),使用后截取掉所有的回车字符
        #str2 = "   Runoob      ";   # 去除首尾空格
        # print(str2.strip());
        #>>Runoob
        line = line.strip()
        # 将整行数据分割成一个元素列表
        listFromLine = line.split('\t')
        returnMat[index,:] = listFromLine[0:3]
        # 保存标签,转化为整型,这个很重要
        classLabelVector.append(int(listFromLine[-1]))
        index +=1
    return returnMat,classLabelVector

#调用函数
matrix,labels = file2matrix('datingTestSet2.txt')
print(matrix)
print(labels[0:20])

输出结果为:

>>[[4.0920000e+04 8.3269760e+00 9.5395200e-01]
 [1.4488000e+04 7.1534690e+00 1.6739040e+00]
 [2.6052000e+04 1.4418710e+00 8.0512400e-01]
 ...
 [2.6575000e+04 1.0650102e+01 8.6662700e-01]
 [4.8111000e+04 9.1345280e+00 7.2804500e-01]
 [4.3757000e+04 7.8826010e+00 1.3324460e+00]]

 >>[3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2, 1, 1, 1, 1, 1, 2, 3]

2.分析数据:画散点图啦

def plot_fig():
    type1_x = []
    type1_y = []
    type2_x = []
    type2_y = []
    type3_x = []
    type3_y = []
    # 这里我们取两个特征
    for i in range(len(labels)):
        if labels[i] == 1:  # 不喜欢
            type1_x.append(matrix[i][1])
            type1_y.append(matrix[i][2])
        if labels[i] == 2:  # 魅力一般
            type2_x.append(matrix[i][1])
            type2_y.append(matrix[i][2])

        if labels[i] == 3:  # 极具魅力

            type3_x.append(matrix[i][1])
            type3_y.append(matrix[i][2])

    plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
    plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

    #画图开始
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(type1_x, type1_y, s=20, alpha=0.8,color='#145b7d',label='不喜欢')
    ax.scatter(type2_x, type2_y, s=20, alpha=0.8,color='#a7324a',label='魅力一般')
    ax.scatter(type3_x, type3_y, s=20, alpha=0.8,color='#585eaa',label='极具魅力')
    plt.xlabel('玩视频优秀消耗的时间')
    plt.ylabel('每周所消耗的冰淇淋')
    plt.legend(loc=1)
    plt.show()
# 调用函数
plot_fig()

kNN算法实践2

图上,密密麻麻一团

3.数据归一化处理

为了避免数值偏大的属性对计算结果的影响,换句话说小美觉得三个特征是同样重要的,做法很简单!
把取值范围处理为01或者11 。可以使用下式转换[newValue=(oldValuemin)/(maxmin),这样就可以,看不懂这个式子,我也没办法了….
好了,写程序了!

def autoNorm(dataSet):
    # axis=0; 每列的最小值/对于多维就是第一维度 ,不理解的好好看[[1,2],[3,4],就是依次比较1和3,2和4
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    m = dataSet.shape[0]
    #tile函数将变量内容复制到输入矩阵同样的维度
    normDataSet = dataSet - np.tile(minVals,(m,1))
    normDataSet = normDataSet/np.tile(ranges,(m,1))

    return normDataSet,ranges,minVals

normMat,ranges,minVals = autoNorm(matrix)
print(normMat)

输出结果为

[[0.44832535 0.39805139 0.56233353]
 [0.15873259 0.34195467 0.98724416]
 [0.28542943 0.06892523 0.47449629]
 ...
 [0.29115949 0.50910294 0.51079493]
 [0.52711097 0.43665451 0.4290048 ]
 [0.47940793 0.3768091  0.78571804]]

4.分类器

def classify0(inX,dataSet,labels,k):
    dataSetSize = dataSet.shape[0] #取第一个维度,计算样本的个数
    diffMat = np.tile(inX,(dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    #argsort()是将元素从小到大排列,提取其对应的index(索引),然后输出
    sortedDisIndicies = distances.argsort()
    #创建一个dict
    classCount ={}
    for i in range(k):
        #获得距离最小前K个的标签
        voteIlabel = labels[sortedDisIndicies[i]]
        # dict.get(key, default=None)函数,key就是dict中的键voteIlabel,
        # 如果不存在则返回一个0并存入dict,如果存在则读取当前值并 + 1;
        # 这样操作后我们可能得到{'A':1,'B':1,'A':1}
        classCount[voteIlabel] = classCount.get(voteIlabel,0) +1

    sortedClassCount = sorted(classCount.items(),\
                       key = operator.itemgetter(1),reverse = True)
    return sortedClassCount[0][0]

5.下面进入测试了,10%的数据进行测试好了,开始

def dataingClassTest():
    hoRatio =0.10
    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)))


dataingClassTest()

输出结果为

# the total error rate is :0.050000,误差还是可以的,当然了,需要指出kNN只适合小型的算法,对于处理图片
# 这样的,估计就够呛了.....

6.约会网站预测函数

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('lilers 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)
    # 默认为1,2,3,索引时需要减去1
    print('You will probably like this person:',resultList[classifierResult-1])
classifyPerson()

结果:

percentage of time spent playing video games?10
frequent flier miles earned per year?2
lilers of ice cream consumed per year?3
You will probably like this person: in small doses

完整代码在这:

#-*- coding: utf-8 -*-
import numpy as np
import matplotlib.font_manager
import matplotlib.pyplot as plt
import operator
myfont = matplotlib.font_manager.FontProperties(fname="Light.ttc")
# 1.收集导入数据
def file2matrix(filename):
    fr = open(filename)
    # readlines()方法用于读取所有行(直到结束符EOF)并返回列表,
    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]
        # 保存标签,转化为整型,这个很重要
        classLabelVector.append(int(listFromLine[-1]))
        index +=1
    return returnMat,classLabelVector

#调用函数
matrix,labels = file2matrix('datingTestSet2.txt')
print(matrix)
print(labels[0:20])
#2.分析数据:画散点图啦
def plot_fig():
    type1_x = []
    type1_y = []
    type2_x = []
    type2_y = []
    type3_x = []
    type3_y = []
    # 这里我们取两个特征
    for i in range(len(labels)):
        if labels[i] == 1:  # 不喜欢
            type1_x.append(matrix[i][1])
            type1_y.append(matrix[i][2])
        if labels[i] == 2:  # 魅力一般
            type2_x.append(matrix[i][1])
            type2_y.append(matrix[i][2])

        if labels[i] == 3:  # 极具魅力

            type3_x.append(matrix[i][1])
            type3_y.append(matrix[i][2])

    plt.rcParams['font.sans-serif'] = ['SimHei']  # 指定默认字体
    plt.rcParams['axes.unicode_minus'] = False  # 解决保存图像是负号'-'显示为方块的问题

    #画图开始
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(type1_x, type1_y, s=20, alpha=0.8,color='#145b7d',label='不喜欢')
    ax.scatter(type2_x, type2_y, s=20, alpha=0.8,color='#a7324a',label='魅力一般')
    ax.scatter(type3_x, type3_y, s=20, alpha=0.8,color='#585eaa',label='极具魅力')
    plt.xlabel('玩视频优秀消耗的时间')
    plt.ylabel('每周所消耗的冰淇淋')
    plt.legend(loc=1)
    plt.show()

# 3.数据归一化处理
def autoNorm(dataSet):
    # axis=0; 每列的最小值/对于多维就是第一维度 ,不理解的好好看[[1,2],[3,4],就是依次比较1和3,2和4
    minVals = dataSet.min(0)
    maxVals = dataSet.max(0)
    ranges = maxVals - minVals
    m = dataSet.shape[0]
    #tile函数将变量内容复制到输入矩阵同样的维度
    normDataSet = dataSet - np.tile(minVals,(m,1))
    normDataSet = normDataSet/np.tile(ranges,(m,1))

    return normDataSet,ranges,minVals

normMat,ranges,minVals = autoNorm(matrix)
print(normMat)

# 4.这是分类器
def classify0(inX,dataSet,labels,k):
    dataSetSize = dataSet.shape[0] #取第一个维度,计算样本的个数
    diffMat = np.tile(inX,(dataSetSize,1)) - dataSet
    sqDiffMat = diffMat**2
    sqDistances = sqDiffMat.sum(axis=1)
    distances = sqDistances**0.5
    #argsort()是将元素从小到大排列,提取其对应的index(索引),然后输出
    sortedDisIndicies = distances.argsort()
    #创建一个dict
    classCount ={}
    for i in range(k):
        #获得距离最小前K个的标签
        voteIlabel = labels[sortedDisIndicies[i]]
        # dict.get(key, default=None)函数,key就是dict中的键voteIlabel,
        # 如果不存在则返回一个0并存入dict,如果存在则读取当前值并 + 1;
        # 这样操作后我们可能得到{'A':1,'B':1,'A':1}
        classCount[voteIlabel] = classCount.get(voteIlabel,0) +1

    sortedClassCount = sorted(classCount.items(),\
                       key = operator.itemgetter(1),reverse = True)
    return sortedClassCount[0][0]


# 5.下面进入测试了,10%的数据进行测试好了,开始   //实际过程中,测试没有必要的,为了对误差有一定了解,还是加上好了
def dataingClassTest():
    hoRatio =0.10
    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)))


dataingClassTest()
# the total error rate is :0.050000,误差还是可以的,当然了,需要指出kNN只适合小型的算法,对于处理图片
# 这样的,估计就够呛了.....

# 7.约会网站预测函数
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('lilers 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)
    # 默认为1,2,3,索引时需要减去1
    print('You will probably like this person:',resultList[classifierResult-1])


classifyPerson()
plot_fig()
相关标签: kNN