运行平台: 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)
部分运行结果截图: