某APP用户一直使用在线约会软件寻找适合本身的约会对象。尽管约会网站会推荐不一样的人选,但她并非喜欢每个人。通过一番总结,她发现曾交往过三种类型的人:python
某APP用户但愿分类软件能够更好地帮助她将匹配对象划分到确切的分类中。此外还能够收集了约会软件不曾记录的数据信息,她认为这些数据更有助于匹配对象的归类。收集的部分信息以下图所示:
数据集下载
样本主要包含如下3种特征:git
在将上述特征数据输入到分类器以前,必须将待处理数据的格式改变为分类器能够接受的格式。github
import numpy as np def file2matrix(filename): """ :param filename: APP用户收集的约会数据的文件名 :return: returnMat: 用户提供的每行数据信息,三列, 分别是每一年得到的飞行常客里程数, 玩视频游戏所耗时间百分比, 每周消费的冰淇淋公升数 classLabelVetor: 用户的评价信息, 通常分为3类(1,2,3) """ fr = open(filename) arrayOfLines = fr.readlines() # print(arrayOfLines) # 得到文件行数; numerOfLines = len(arrayOfLines) # 建立要返回的Numpy矩阵; returnMat = np.zeros((numerOfLines, 3)) # 解析文件数据到矩阵中; classLabelVetor = [] index = 0 for line in arrayOfLines: line = line.strip() listFromLine = line.split('\t') returnMat[index, :] = listFromLine[0:3] classLabelVetor.append(listFromLine[-1]) index += 1 return returnMat, classLabelVetor print(file2matrix('data/datingTestSet2'))
返回的值显示: 算法
使用Matplotlib库图形化清晰地标识了三个不一样的样本分类区域,具备不一样爱好的人其类别区域也不一样。app
def draw_pic(datingDataMat, datingLabels): """ 每一年获取的飞行常客里程数与每周所消费的冰淇淋公升数”构成的散点图。 :param datingDataMat: :param datingLabels: :return: """ # 中文显示乱码问题; myfont = font_manager.FontProperties(fname="/usr/share/fonts/cjkuni-uming/uming.ttc", size=12) # 建立画布 fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:, 0], datingDataMat[:, 2], 15 * datingLabels, datingLabels) plt.xlabel("每一年的飞行里程数", fontproperties=myfont) plt.ylabel("每周消费的冰淇淋公升数", fontproperties=myfont) plt.grid(alpha=0.5) plt.show()
def autoNorm(dataSet): """ 归一化数值, :param dataSet:用户提供的每行数据信息,三列; :return: normDataSet: 归一化的特征信息; maxVals:每一个特征数据的最大值; """ # 获取每一个特征数据的最大值; maxVals = dataSet.max(0) # 获取样本个数; m = dataSet.shape[0] # 根据公式生成归一化的特征信息; normDataSet = dataSet / np.tile(maxVals, (m, 1)) return normDataSet, maxVals
对未知类别属性的数据集中的每一个点依次执行如下操做, 与上一个案例代码相同:
(1) 计算已知类别数据集中的点与当前点之间的距离;
(2) 按照距离递增次序排序;
(3) 选取与当前点距离最小的k个点;
(4) 肯定前k个点所在类别的出现频率;
(5) 返回前k个点出现频率最高的类别做为当前点的预测分类。机器学习
def classify(inX, dataSet, labels, k): """ :param inX: 要预测的数据 :param dataSet: 咱们要传入的已知数据集 :param labels: 咱们要传入的标签 :param k: KNN里的k, 也就是说咱们要选几个近邻 :return: 排序的结果 """ dataSetSize = dataSet.shape[0] # (6,2) 6 # tile会重复inX, 把他重复成(datasetsize, 1)型的矩阵 # print(inX) # (x1 - y1), (x2- y2) diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet # 平方 sqDiffMat = diffMat ** 2 # 相加, axis=1 行相加 sqDistance = sqDiffMat.sum(axis=1) # 开根号 distances = sqDistance ** 0.5 # print(distances) # 排序 输出的是序列号index,并非值 sortedDistIndicies = distances.argsort() # print(sortedDistIndicies) classCount = {} for i in range(k): voteLabel = labels[sortedDistIndicies[i]] classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 # print(classCount) sortedClassCount = sorted(classCount.items(), key=lambda d: float(d[1]), reverse=True) return sortedClassCount[0]
若是分类器的正确率知足要求,就能够使用这个软件来处理约会网站提供的约会名单了。机器学习算法一个很重要的工做就是评估算法的正确率,一般咱们只提供已有数据的90%做为训练样原本训练分类器,而使用其他的10%数据去测试分类器,检测分类器的正确率。学习
def datingClassTest(): """ 分类器针对约会网站的测试代码, 获取错误率; :return: """ hoRatio = 0.10 datingDataMat, datingLabels = file2matrix('data/datingTestSet2') normDataSet, maxVals = autoNorm(datingDataMat) # 样本个数 m = normDataSet.shape[0] # 测试集个数; numTestVecs = int(m*hoRatio) errorCount = 0.0 for i in range(numTestVecs): classiferResult = classify(normDataSet[i, :], normDataSet[numTestVecs:m, :], datingLabels[numTestVecs:m], 3) # print(classiferResult) if classiferResult != datingLabels[i]: errorCount += 1 print("正确结果:", datingLabels[i]) print("预测结果:", classiferResult) # print("错误个数:", errorCount) return errorCount
def classifyPerson(Person): """ 使用这个分类器为某APP用户来对人们分类。 :param Person: :return: """ datingDataMat, datingLabels = file2matrix('data/datingTestSet2') normDataSet, maxVals = autoNorm(datingDataMat) classiferResult = classify(Person / maxVals, normDataSet, datingLabels, 3) if classiferResult == '1': print("不喜欢") elif classiferResult == '2': print("有一点喜欢") else: print("很是喜欢")
# encoding:utf-8 """ KNN实现,基于KNN分类的约会网站配对改进算法 """ import numpy as np import matplotlib.pyplot as plt from matplotlib import font_manager def file2matrix(filename): """ :param filename: APP用户收集的约会数据的文件名 :return: returnMat: 用户提供的每行数据信息,三列, 分别是每一年得到的飞行常客里程数, 玩视频游戏所耗时间百分比, 每周消费的冰淇淋公升数 classLabelVetor: 用户的评价信息, 通常分为3类(1,2,3) """ fr = open(filename) arrayOfLines = fr.readlines() # print(arrayOfLines) # 得到文件行数; numerOfLines = len(arrayOfLines) # 建立要返回的Numpy矩阵; returnMat = np.zeros((numerOfLines, 3)) # 解析文件数据到矩阵中; classLabelVetor = [] index = 0 for line in arrayOfLines: line = line.strip() listFromLine = line.split('\t') returnMat[index, :] = listFromLine[0:3] classLabelVetor.append(listFromLine[-1]) index += 1 return returnMat, classLabelVetor def autoNorm(dataSet): """ 归一化数值, 计算样本3和样本4之间的距离: [(0-67)**2 + (20000 - 32 000)**2 + (1.1 - 0.1)**2]**0.5 问题: 飞行常客里程数对于计算结果的影响将远远大于其余两个特征的影响 解决方式: 处理不一样取值范围的特征值时, 一般采用的方法是将数值归一化,如将取值范围处理为0到1或者-1到1之间。 归一化公式: newValue = oldValue / max :param dataSet:用户提供的每行数据信息,三列; :return: normDataSet: 归一化的特征信息; maxVals:每一个特征数据的最大值; """ # 获取每一个特征数据的最大值; maxVals = dataSet.max(0) # 获取样本个数; m = dataSet.shape[0] # 根据公式生成归一化的特征信息; normDataSet = dataSet / np.tile(maxVals, (m, 1)) return normDataSet, maxVals def draw_pic(datingDataMat, datingLabels): """ 每一年获取的飞行常客里程数与每周所消费的冰淇淋公升数”构成的散点图。 :param datingDataMat: :param datingLabels: :return: """ # 中文显示乱码问题; myfont = font_manager.FontProperties(fname="/usr/share/fonts/cjkuni-uming/uming.ttc", size=12) # 建立画布 fig = plt.figure() ax = fig.add_subplot(111) ax.scatter(datingDataMat[:, 0], datingDataMat[:, 2], 15 * datingLabels, datingLabels) plt.xlabel("每一年的飞行里程数", fontproperties=myfont) plt.ylabel("每周消费的冰淇淋公升数", fontproperties=myfont) plt.grid(alpha=0.5) plt.show() def classify(inX, dataSet, labels, k): """ :param inX: 要预测的数据 :param dataSet: 咱们要传入的已知数据集 :param labels: 咱们要传入的标签 :param k: KNN里的k, 也就是说咱们要选几个近邻 :return: 排序的结果 """ dataSetSize = dataSet.shape[0] # (6,2) 6 # tile会重复inX, 把他重复成(datasetsize, 1)型的矩阵 # print(inX) # (x1 - y1), (x2- y2) diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet # 平方 sqDiffMat = diffMat ** 2 # 相加, axis=1 行相加 sqDistance = sqDiffMat.sum(axis=1) # 开根号 distances = sqDistance ** 0.5 # print(distances) # 排序 输出的是序列号index,并非值 sortedDistIndicies = distances.argsort() # print(sortedDistIndicies) classCount = {} for i in range(k): voteLabel = labels[sortedDistIndicies[i]] classCount[voteLabel] = classCount.get(voteLabel, 0) + 1 # print(classCount) sortedClassCount = sorted(classCount.items(), key=lambda d: float(d[1]), reverse=True) return sortedClassCount[0][0] def datingClassTest(): """ 分类器针对约会网站的测试代码, 获取错误率; :return: """ hoRatio = 0.10 datingDataMat, datingLabels = file2matrix('data/datingTestSet2') normDataSet, maxVals = autoNorm(datingDataMat) # 样本个数 m = normDataSet.shape[0] # 测试集个数; numTestVecs = int(m*hoRatio) errorCount = 0.0 for i in range(numTestVecs): classiferResult = classify(normDataSet[i, :], normDataSet[numTestVecs:m, :], datingLabels[numTestVecs:m], 3) # print(classiferResult) if classiferResult != datingLabels[i]: errorCount += 1 print("正确结果:", datingLabels[i]) print("预测结果:", classiferResult) # print("错误个数:", errorCount) return errorCount def classifyPerson(Person): """ 使用这个分类器为某APP用户来对人们分类。 :param Person: :return: """ datingDataMat, datingLabels = file2matrix('data/datingTestSet2') normDataSet, maxVals = autoNorm(datingDataMat) classiferResult = classify(Person / maxVals, normDataSet, datingLabels, 3) if classiferResult == '1': print("不喜欢") elif classiferResult == '2': print("有一点喜欢") else: print("很是喜欢") if __name__ == '__main__': # personData = [30000, 10, 1.3] personData = [40920, 8.326976, 0.953952] classifyPerson(personData)