读取文件,展示三个特征:不喜欢,一般,喜欢 与三个条件:飞机里程,游戏时间,每周消费的冰激凌 之间的关系
import numpy as np
def file2matrix(filename):
fr = open(filename)
arrayOLines = fr.readlines() #按行读取文件
numberOFLines = len(arrayOLines) #获取文件的行数
returnMat = np.zeros((numberOFLines,3)) #初始化数据集的数组
classLabelVector = []
index = 0
for line in arrayOLines:
line = line.strip() #strip()返回移除字符串头尾指定的字符生成的新字符串。中间的不移除。默认为空格
listFromLine = line.split('\t') #按照制表符将字符串转化为数组
returnMat[index,:] = listFromLine[0:3] #listFromLine[0:3]是获取每行的前三个元素,returnMat[index,:] index为行索引,将数据赋值给代表该行
classLabelVector.append(int(listFromLine[-1])) #每行的最后一个元素,即特征
index+=1
return returnMat, classLabelVector
import matplotlib
import matplotlib.pyplot as plt
fig = plt.figure()
ax = fig.add_subplot(221) #add_subplot(nmi) 代表将画布分割成n行m列,画在从左到右从上到下的第i块
ax2 = fig.add_subplot(222)
ax3 = fig.add_subplot(223)
ax4 = fig.add_subplot(224)
ax.scatter(datingDataMat[:,1], datingDataMat[:,2])#[:,1],[:,2]代表datingDataMat的第二、三列数据
ax2.scatter(datingDataMat[:,1], datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels)) #数据乘以特征值,更好的区别特征数据
ax3.scatter(datingDataMat[:,0], datingDataMat[:,2],15.0*array(datingLabels),15.0*array(datingLabels))
ax4.scatter(datingDataMat[:,0], datingDataMat[:,1],15.0*array(datingLabels),15.0*array(datingLabels))
plt.show()
分类效果图
将数据归一化:飞行里程数太大,其对计算结果的影响远大于玩游戏的时间和每周消费的冰激凌公升数。但是这三个特征应该是同等重要的,所以要对数据进行归一化
def autoNorm(dataSet):
minVals = dataSet.min(0) #获取最小值(数组最小值?) [ 0. 0. 0.001156]
maxVals = dataSet.max(0) #获取最大值(数组最大值?) [ 9.12730000e+04 2.09193490e+01 1.69551700e+00]
ranges = maxVals - minVals #获取最大,最小的差值 [ 9.12730000e+04 2.09193490e+01 1.69436100e+00]
normDataSet = np.zeros(shape(dataSet)) #初始化数据集
print(type(dataSet))
m = dataSet.shape[0] #获取数据集的行数1000 在方法外调用报错'tuple' object is not callable?
normDataSet = dataSet - tile(minVals, (m, 1)) #将最小值所在的数组复制1000份,与原数据集行数列数相同,然后相减,获得差值
normDataSet = normDataSet/tile(ranges, (m, 1)) #将上面获得的差值除以整个数据集的最大最小的差值,完成归一化
return normDataSet, ranges, minVals
normDataSet, ranges, minVals = autoNorm(datingDataMat)
之前的数据集
[[ 4.09200000e+04 8.32697600e+00 9.53952000e-01]
[ 1.44880000e+04 7.15346900e+00 1.67390400e+00]
[ 2.60520000e+04 1.44187100e+00 8.05124000e-01]
…,
[ 2.65750000e+04 1.06501020e+01 8.66627000e-01]
[ 4.81110000e+04 9.13452800e+00 7.28045000e-01]
[ 4.37570000e+04 7.88260100e+00 1.33244600e+00]]
归一化后的数据集
[[ 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]]
K-近邻的算法函数
import operator
#获取与原数据差值最小的k个数据所在的类别的个数,个数最多的作为它的特征
def classify0(inX, dataSet, labels , k): #四个参数分别为用于分类的输入变量,输入的样本训练集,特征向量,k近邻的k值
dataSetSize = dataSet.shape[0] # 判断二维数组的行数,也就是标签/样本的个数
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet #tile([0,0],(4,1))创建二维数组,[[0, 0],[0, 0],[0, 0],[0, 0]],减去原数组,获取差值
sqDiffMat = diffMat**2 #差值为矩阵,矩阵*矩阵,对应元素相乘
sqDistance = sqDiffMat.sum(axis=1)#将一个矩阵的每一行元素相加
distance = sqDistance**0.5 #[ 1.48660687 1.41421356 0. 0.1 ]
sortedDistIndicies= sqDistance.argsort()#argsort()返回从小到大的索引值 [2 3 1 0]
classCount = {}
for i in range(k):
voteIlabel = labels[sortedDistIndicies[i]]
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1 #classCount中如果有voteIlabel就+1,初始值为1(因为后边+1)。
sortedClassCount = sorted(classCount.items(),key = operator.itemgetter(1), reverse = True) #降序排列,key用来提取用于比较的值
return sortedClassCount[0][0]
验证分类器
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat) #将数据归一化处理
m = normMat.shape[0] #获取数据的行数1000
numTestVecs = int(m*hoRatio) #测试集的数据数量100
errorCount = 0.0
for i in range(numTestVecs): #循环100次,将前100条数据作为测试集,后边的900条数据作为样本训练集,并选取3个结果最接近的值,3个数据中数量最多的的特征作为类别
classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :],datingLabels[numTestVecs:m], 5)
print("the classfier 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)))
执行方法
def datingClassTest():
hoRatio = 0.10
datingDataMat,datingLabels = file2matrix('datingTestSet2.txt')
normMat, ranges, minVals = autoNorm(datingDataMat) #将数据归一化处理
m = normMat.shape[0] #获取数据的行数1000
numTestVecs = int(m*hoRatio) #测试集的数据数量100
errorCount = 0.0
for i in range(numTestVecs): #循环100次,将前100条数据作为测试集,后边的900条数据作为样本训练集,并选取3个结果最接近的值,3个数据中数量最多的的特征作为类别
classifierResult = classify0(normMat[i, :], normMat[numTestVecs:m, :],datingLabels[numTestVecs:m], 5)
print("the classfier 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)))
错误率为5% 更改测试集数量 将hoRatio改为0.05,错误率下降到2%;改回hoRatio为0.10,将k值增多到5,错误率下降到2%。符合逻辑
约会网站预测函数
根据输入的三个特征值,进行判断是否会对他产生兴趣
def classifyPerson():
resultList = ['not at all', 'in small doses', 'in large doses']
percentTages = float(input("percentage of time spent game? "))
ffMiles = float(input("frequent filer miles earned per year?"))
iceCream = float(input("liters of ice cream consumed per year?"))
datingDataMat, datingLabels = file2matrix('datingTestSet2.txt') #获取数据集
normMat, ranges, minVals = autoNorm(datingDataMat) #归一化数据集
inArr = array([percentTages,ffMiles,iceCream]) #输入的数据
classifierResult = classify0((inArr-minVals)/ranges, normMat, datingLabels,3) #第一个参数是归一化输入数据
print("youu will probably like this person:",resultList[classifierResult -1])
终于完成了‘K-近邻改进约会网站的配对效果‘这一项目,以上代码大部分都有注释,当然有些注释可能不对(毕竟我也是新手一枚),欢迎大家前来讨论