目录
kNN算法项目实战———改进约会网站的配对效果
1.项目背景
2.项目数据
3.K-邻近算法的一般流程
4.项目步骤及代码实现
1.k--邻近算法
2.Python 分析处理文本数据
3.数据可视化
4.归一化
5.测试算法
6.预测
7.完整代码
海伦女士一直使用在线约会网站寻找适合自己的约会对象。尽管约会网站会推荐不同的不选,但她并不是喜欢每一个人。经过一番总结,她发现自己交往过的人可以进行如下分类:
海伦收集约会数据已经有了一段时间,她把这些数据存放在文本文件datingTestSet.txt中,每个样本占据一行,总共有1000行。
https://github.com/Asia-Lee/KNN/blob/master/datingTestSet.txt --数据集下载
海伦收集的样本数据主要包含以下3种特征
数据格式如下:
(1)收集数据:提供文本文件
(2)准备数据:使用Python解析文本文件
(3)分析数据:使用Matplotlib画二维扩散图
(4)测试算法:使用文本文件的部分数据作为测试样本,计算错误率
(5)使用算法:错误率再可接受范围内,就可以使用k-邻近算法进行分类
# -*- coding:utf-8 -*- import numpy as np import operator """ k-邻近算法: inX 是待分类点, dataSet是训练集, labels是标签向量, k是选取的最近的样本点的个数 """ def Classify0(inX, dataSet, labels, k): # 得到训练集的行数 dataSetSize = dataSet.shape[0] # 计算待分类点与训练集中每个样本的距离并排序 diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet sqdiffMat = diffMat ** 2 sqDistance = sqdiffMat.sum(axis=1) distance = sqDistance ** 0.5 sortedDistIndicies = distance.argsort() classCount = {} # 存放选取的样本所出现分类的频率 for i in range(k): voteIlabels = labels[sortedDistIndicies[i]] # 计算筛选样本点标签的频率 classCount[voteIlabels] = classCount.get(voteIlabels, 0) + 1 # 频率排序并返回最大频率的标签作为预测标签 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0]
""" 对文本进行分析处理 """ def file2matrix(filename): fr = open(filename) # 按行获取文本信息 arrayOfLines = fr.readlines() # 获取行数 numberOfLines = len(arrayOfLines) # 创建返回矩阵(1000 * 3) 以及 标签列表 (1000 * 1) returnMat = np.zeros((numberOfLines, 3)) classLabelVector = [] index = 0 for line in arrayOfLines: line = line.strip() # 删除空白符,如 '\t','\r','\n' listFromLines = line.split('\t') # 按制表符对文本进行分割(1000 * 4) # 将样本特征存放至返回矩阵,将文本标签处理成数字标签后放入标签列表 returnMat[index, :] = listFromLines[0:3] index += 1 if listFromLines[-1] == 'didntLike': classLabelVector.append(1) if listFromLines[-1] == 'smallDoses': classLabelVector.append(2) if listFromLines[-1] == 'largeDoses': classLabelVector.append(3) return returnMat, classLabelVector # 测试文本读取情况,输出前20行 datingDataMat, datingLabels = file2matrix('datingTestSet1.txt') for i in range(20): print(datingDataMat[i], datingLabels[i])
""" 数据可视化处理 """ import matplotlib.lines as mlines import matplotlib.pyplot as plt import matplotlib as mpl def showdata(datingDataMat, datingLabels): # 设置字体格式,处理图中文字符显示问题 mpl.rcParams['font.sans-serif'] = ['SimHei'] mpl.rcParams['axes.unicode_minus'] = False # 把画布分割成 2行2列,不共享x 轴, y 轴, 画布大小为(13,9) # 当nrows=2, ncols=2, 代表fig画布分为4个区域,axs[0][0]代表第一个区域 fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13, 9)) LabelColors = [] for i in datingLabels: if i == 1: LabelColors.append('black') if i == 2: LabelColors.append('orange') if i == 3: LabelColors.append('red') # 画出散点图1,第一列,第二列作为x, y 轴 axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=LabelColors, s=15, alpha=0.5) axs0_title_text = axs[0][0].set_title('每年获得的飞行常客里程数与玩视频游戏所消耗时间占比') axs0_xlabel_text = axs[0][0].set_xlabel('每年获得的飞行常客里程数') axs0_ylabel_text = axs[0][0].set_ylabel('每年玩视频游戏所消耗时间占比') plt.setp(axs0_title_text, size=12, weight='bold', color='red') plt.setp(axs0_xlabel_text, size=10, weight='bold', color='black') plt.setp(axs0_ylabel_text, size=10, weight='bold', color='black') # 画出散点图2,第一列, 第三列作为x, y 轴 axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=LabelColors, s=15, alpha=0.5) axs1_title_text = axs[0][1].set_title('每年获得的飞行常客里程数与每周消费冰激凌公升数') axs1_xlabel_text = axs[0][1].set_xlabel('每年获得的飞行常客数') axs1_ylabel_text = axs[0][1].set_ylabel('每周消费冰激凌公升数') plt.setp(axs1_title_text, size=12, weight='bold', color='red') plt.setp(axs1_xlabel_text, size=10, weight='bold', color='black') plt.setp(axs1_ylabel_text, size=10, weight='bold', color='black') # 画出散点图3, 第二列, 第三列作为x, y 轴 axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=LabelColors, s=15, alpha=0.5) axs2_title_text = axs[1][0].set_title('每年玩视频游戏所消耗时间占比与每周消费的冰激凌公升数') axs2_xlabel_text = axs[1][0].set_xlabel('每年玩视频游戏所消耗时间占比') axs2_ylabel_text = axs[1][0].set_ylabel('每周消费的冰激凌公升数') plt.setp(axs2_xlabel_text, size=12, weight='bold', color='red') plt.setp(axs2_xlabel_text, size=10, weight='bold', color='black') plt.setp(axs2_ylabel_text, size=10, weight='bold', color='black') # 设置图例 didntLike = mlines.Line2D([], [], color='black', marker='.', markersize=6, label='不喜欢') smallDoses = mlines.Line2D([], [], color='orange', marker='.', markersize=6, label='魅力一般') largeDoses = mlines.Line2D([], [], color='orange', marker='.', markersize=6, label='极具魅力') # 添加图例 axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses]) axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses]) axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses]) # 显示图像 plt.show() # 测试可视化结果 datingDataMat, datingLabels = file2matrix('datingTestSet1.txt') showdata(datingDataMat, datingLabels)
""" 归一化处理 """ def autoNorm(dataSet): # 获取每列中的最小,最大值 共(1 * 3)个 minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals # 创建和dataSet相同的0矩阵 normDataSet = np.zeros(np.shape(dataSet)) m = dataSet.shape[0] # 查看第一维长度 # dataSet 中每个样本 - 最小值 normDataSet = dataSet - np.tile(minVals, (m, 1)) # 每个样本 / 范围 normDataSet = normDataSet/np.tile(ranges, (m, 1)) # 返回归一化后的数据,范围,每列最小值 return normDataSet, ranges, minVals # 测试归一化结果 datingDataSet, datingLabels = file2matrix('datingTestSet1.txt') for i in range(10): print(datingDataSet[i], datingLabels[i]) normDataSet, ranges, minVals = autoNorm(datingDataSet) for i in range(10): print(normDataSet[i], datingLabels[i])
""" 测试算法 """ def datingClassTest(): hoRatio = 0.10 # 测试集所占比例 datingDataSet, datingLabels = file2matrix('datingTestSet1.txt') # 归一化 normDataSet, ranges, minVals = autoNorm(datingDataSet) m = normDataSet.shape[0] # 测试集数量 numTestVecs = int(m*hoRatio) errorCount = 0 for i in range(numTestVecs): # 利用训练数据训练分类器,再用分类器预测测试集结果 classifierResult = Classify0(normDataSet[i, :], normDataSet[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 print("The total error rate is : %f" % (errorCount/float(numTestVecs)))
""" 预测 """ def classifyPerson(): resultList = ['不喜欢', '有点好感', '非常喜欢'] percentTats = float(input("Percentage of time spent playing video game?")) ffMiles = float(input("Frequent flier miles earned per year?")) iceCream = float(input("liters of ice cream consumed per week?")) datingDataSet, datingLabels = file2matrix('datingTestSet1.txt') normDataSet, ranges, minVals = autoNorm(datingDataSet) inArr = np.array([ffMiles, percentTats, iceCream]) # 待分类特征向量 # 输入数据归一化 classifierResult = Classify0((inArr-minVals)/ranges, normDataSet, datingLabels, 3) print("You will probably like this person: ", resultList[classifierResult-1])
# -*- coding:utf-8 -*- import numpy as np import operator """ k-邻近算法: inX 是待分类点, dataSet是训练集, labels是标签向量, k是选取的最近的样本点的个数 """ def Classify0(inX, dataSet, labels, k): # 得到训练集的行数 dataSetSize = dataSet.shape[0] # 计算待分类点与训练集中每个样本的距离并排序 diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet sqdiffMat = diffMat ** 2 sqDistance = sqdiffMat.sum(axis=1) distance = sqDistance ** 0.5 sortedDistIndicies = distance.argsort() classCount = {} # 存放选取的样本所出现分类的频率 for i in range(k): voteIlabels = labels[sortedDistIndicies[i]] # 计算筛选样本点标签的频率 classCount[voteIlabels] = classCount.get(voteIlabels, 0) + 1 # 频率排序并返回最大频率的标签作为预测标签 sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) return sortedClassCount[0][0] """ 对文本进行分析处理 """ def file2matrix(filename): fr = open(filename) # 按行获取文本信息 arrayOfLines = fr.readlines() # 获取行数 numberOfLines = len(arrayOfLines) # 创建返回矩阵(1000 * 3) 以及 标签列表 (1000 * 1) returnMat = np.zeros((numberOfLines, 3)) classLabelVector = [] index = 0 for line in arrayOfLines: line = line.strip() # 删除空白符,如 '\t','\r','\n' listFromLines = line.split('\t') # 按制表符对文本进行分割(1000 * 4) # 将样本特征存放至返回矩阵,将文本标签处理成数字标签后放入标签列表 returnMat[index, :] = listFromLines[0:3] index += 1 if listFromLines[-1] == 'didntLike': classLabelVector.append(1) if listFromLines[-1] == 'smallDoses': classLabelVector.append(2) if listFromLines[-1] == 'largeDoses': classLabelVector.append(3) return returnMat, classLabelVector # # 测试文本读取情况,输出前20行 # datingDataMat, datingLabels = file2matrix('datingTestSet1.txt') # for i in range(20): # print(datingDataMat[i], datingLabels[i]) """ 数据可视化处理 """ import matplotlib.lines as mlines import matplotlib.pyplot as plt import matplotlib as mpl def showdata(datingDataMat, datingLabels): # 设置字体格式,处理图中文字符显示问题 mpl.rcParams['font.sans-serif'] = ['SimHei'] mpl.rcParams['axes.unicode_minus'] = False # 把画布分割成 2行2列,不共享x 轴, y 轴, 画布大小为(13,9) # 当nrows=2, ncols=2, 代表fig画布分为4个区域,axs[0][0]代表第一个区域 fig, axs = plt.subplots(nrows=2, ncols=2, sharex=False, sharey=False, figsize=(13, 9)) LabelColors = [] for i in datingLabels: if i == 1: LabelColors.append('black') if i == 2: LabelColors.append('orange') if i == 3: LabelColors.append('red') # 画出散点图1,第一列,第二列作为x, y 轴 axs[0][0].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 1], color=LabelColors, s=10, alpha=0.5) axs0_title_text = axs[0][0].set_title('每年获得的飞行常客里程数与玩视频游戏所消耗时间占比') axs0_xlabel_text = axs[0][0].set_xlabel('每年获得的飞行常客里程数') axs0_ylabel_text = axs[0][0].set_ylabel('每年玩视频游戏所消耗时间占比') plt.setp(axs0_title_text, size=10, weight='bold', color='red') plt.setp(axs0_xlabel_text, size=8, weight='bold', color='black') plt.setp(axs0_ylabel_text, size=8, weight='bold', color='black') # 画出散点图2,第一列, 第三列作为x, y 轴 axs[0][1].scatter(x=datingDataMat[:, 0], y=datingDataMat[:, 2], color=LabelColors, s=10, alpha=0.5) axs1_title_text = axs[0][1].set_title('每年获得的飞行常客里程数与每周消费冰激凌公升数') axs1_xlabel_text = axs[0][1].set_xlabel('每年获得的飞行常客数') axs1_ylabel_text = axs[0][1].set_ylabel('每周消费冰激凌公升数') plt.setp(axs1_title_text, size=10, weight='bold', color='red') plt.setp(axs1_xlabel_text, size=8, weight='bold', color='black') plt.setp(axs1_ylabel_text, size=8, weight='bold', color='black') # 画出散点图3, 第二列, 第三列作为x, y 轴 axs[1][0].scatter(x=datingDataMat[:, 1], y=datingDataMat[:, 2], color=LabelColors, s=10, alpha=0.5) axs2_title_text = axs[1][0].set_title('每年玩视频游戏所消耗时间占比与每周消费的冰激凌公升数') axs2_xlabel_text = axs[1][0].set_xlabel('每年玩视频游戏所消耗时间占比') axs2_ylabel_text = axs[1][0].set_ylabel('每周消费的冰激凌公升数') plt.setp(axs2_title_text, size=10, weight='bold', color='red') plt.setp(axs2_xlabel_text, size=8, weight='bold', color='black') plt.setp(axs2_ylabel_text, size=8, weight='bold', color='black') # 设置图例 didntLike = mlines.Line2D([], [], color='black', marker='.', markersize=6, label='不喜欢') smallDoses = mlines.Line2D([], [], color='orange', marker='.', markersize=6, label='魅力一般') largeDoses = mlines.Line2D([], [], color='orange', marker='.', markersize=6, label='极具魅力') # 添加图例 axs[0][0].legend(handles=[didntLike, smallDoses, largeDoses]) axs[0][1].legend(handles=[didntLike, smallDoses, largeDoses]) axs[1][0].legend(handles=[didntLike, smallDoses, largeDoses]) # 显示图像 plt.show() # # # 测试可视化结果 # # datingDataMat, datingLabels = file2matrix('datingTestSet1.txt') # showdata(datingDataMat, datingLabels) """ 归一化处理 """ def autoNorm(dataSet): # 获取每列中的最小,最大值 共(1 * 3)个 minVals = dataSet.min(0) maxVals = dataSet.max(0) ranges = maxVals - minVals # 创建和dataSet相同的0矩阵 normDataSet = np.zeros(np.shape(dataSet)) m = dataSet.shape[0] # 查看第一维长度 # dataSet 中每个样本 - 最小值 normDataSet = dataSet - np.tile(minVals, (m, 1)) # 每个样本 / 范围 normDataSet = normDataSet/np.tile(ranges, (m, 1)) # 返回归一化后的数据,范围,每列最小值 return normDataSet, ranges, minVals # # 测试归一化结果 # datingDataSet, datingLabels = file2matrix('datingTestSet1.txt') # for i in range(10): # print(datingDataSet[i], datingLabels[i]) # # normDataSet, ranges, minVals = autoNorm(datingDataSet) # for i in range(10): # print(normDataSet[i], datingLabels[i]) # """ 测试算法 """ def datingClassTest(): hoRatio = 0.10 # 测试集所占比例 datingDataSet, datingLabels = file2matrix('datingTestSet1.txt') # 归一化 normDataSet, ranges, minVals = autoNorm(datingDataSet) m = normDataSet.shape[0] # 测试集数量 numTestVecs = int(m*hoRatio) errorCount = 0 for i in range(numTestVecs): # 利用训练数据训练分类器,再用分类器预测测试集结果 classifierResult = Classify0(normDataSet[i, :], normDataSet[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 print("The total error rate is : %f" % (errorCount/float(numTestVecs))) """ 预测 """ def classifyPerson(): resultList = ['不喜欢', '有点好感', '非常喜欢'] percentTats = float(input("Percentage of time spent playing video game?")) ffMiles = float(input("Frequent flier miles earned per year?")) iceCream = float(input("liters of ice cream consumed per week?")) datingDataSet, datingLabels = file2matrix('datingTestSet1.txt') normDataSet, ranges, minVals = autoNorm(datingDataSet) inArr = np.array([ffMiles, percentTats, iceCream]) # 待分类特征向量 # 输入数据归一化 classifierResult = Classify0((inArr-minVals)/ranges, normDataSet, datingLabels, 3) print("You will probably like this person: ", resultList[classifierResult-1]) classifyPerson()