修改了字典的创建函数:classCount[voteIlabel] = classCount.setdefault(voteIlabel, 0) + 1
修改了字典的排序方式:sorted(classCount.items(), key=operator.itemgetter(1), reverse=True)
添加了绘表函数:def pltFig(datingDataMat, datingLabels)
利用了numpy的广播机制:diffMat = inX - dataSet 而非 diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
将英文输入打印,改成中文输入打印
修改了每年获得飞行常客里程数、玩视频游戏所耗时间、每周消费冰淇淋公升数的数据输入方式
将原码的m变量改名为dataSetRowNum变量
在def autoNorm(dataSet)返回的变量中添加了dataSetRowNum变量,减少了原码的冗余
import numpy as np
import operator
import matplotlib
import matplotlib.pyplot as plt
'''绘制表格'''
def pltFig(datingDataMat, datingLabels):
# 创建画表
# plt.figure()中有个参数figsize = (2,2),用来设置表格的大小
fig = plt.figure(figsize=(10, 10))
# 分配表位
ax1 = fig.add_subplot(311)
ax2 = fig.add_subplot(312)
ax3 = fig.add_subplot(313)
# 导入数据
ax1.scatter(datingDataMat[:, 0], datingDataMat[:, 1], 15.0 * np.array(datingLabels), 15.0 * np.array(datingLabels))
ax2.scatter(datingDataMat[:, 0], datingDataMat[:, 2], 15.0 * np.array(datingLabels), 15.0 * np.array(datingLabels))
ax3.scatter(datingDataMat[:, 1], datingDataMat[:, 2], 15.0 * np.array(datingLabels), 15.0 * np.array(datingLabels))
ax1.set(ylabel='玩视频游戏所耗时间', xlabel='每年获得飞行常客里程数')
ax2.set(ylabel='每周消费冰淇淋公升数', xlabel='每年获得飞行常客里程数')
ax3.set(ylabel='每周消费冰淇淋公升数', xlabel='玩视频游戏所耗时间')
plt.show()
#创造矩阵
def createDataSet():
# 生成矩阵
group = np.array([1., 1.1], [1., 1.], [0, 0], [0, 0.1])
# 标记特征值
labels = ['a', 'a', 'b', 'b']
return group, labels
'''inX为样本,dataSet为训练集,labels为标签向量,k为聚类个数'''
def classIfY0(inX, dataSet, labels, k):
# 返回dataSet训练集的行数;*shape[0]为行数、shape[1]为列数
dataSetSize = dataSet.shape[0]
# np.tile为拷贝,将 inX 拷贝行dataSetSize次,列1次
# 拷贝完的 inX' 减去dataSet矩阵
# 其实python具有广播机制也是可以的
# diffMat = inX - dataSet
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
# 做完减法后得平方
sqDiffMat = diffMat ** 2
# sqDiffMat矩阵按行相加,得到dataSetSize列矩阵;*axis=1按行相加、axis=0按列相加
sqDistances = sqDiffMat.sum(axis=1)
# 对sqDistances矩阵开方
distances = sqDistances**0.5
# 从小到大排列,提取其对应的index(索引)
sortedDistIndicies = distances.argsort()
# 创建空字典,用来记录K个邻居点的特征值名称(键)-数量(值)
classCount = {
}
for i in range(k):
# 找到[0, k)区间较小的值所对应的特征值
# sortedDistIndicies[i]返回数值较小的索引号
voteIlabel = labels[sortedDistIndicies[i]]
# 在字典中查找voteIlabel键的特征值个数
# 若找到则返回voteIlabel键下对应的值并加1;
# 若找不出,则新建voteIlabel(键)- 0(值)并加1
classCount[voteIlabel] = classCount.setdefault(voteIlabel, 0) + 1 #**
# classCount.items()返回键值对迭代器,字典形式:{'a':1,'b':3}-》列表形式[('a',1),('b',3)]
# key=operator.itemgetter(1)使用元素第二维的数据进行排序
# reverse=True决定了排序方式,从大到小
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #**
return sortedClassCount[0][0]
'''读取训练文件'''
def file2matrix(fileName):
# 读取文件
with open(fileName) as fr:
# 按行读取
arrayOlinges = fr.readlines()
# 获得长度
numberOfLines = len(arrayOlinges)
# 生成N * 3的矩阵
returnMat = np.zeros((numberOfLines, 3))
classLabelVector = []
index = 0
for line in arrayOlinges:
line = line.strip()
# 将行数据写入列表中
listFromLine = line.split('\t')
# 将文件的前三列写入矩阵中
returnMat[index, :] = listFromLine[0: 3]
# 记录第四列内容
classLabelVector.append(int(listFromLine[-1]))
# 行+1
index += 1
#print(line)
'''返回前三列的数据矩阵和一列特征矩阵'''
return returnMat, classLabelVector
'''归一化特征值'''
def autoNorm(dataSet):
# 得到最大、最小值和差值
minVals = dataSet.min(0)
maxVals = dataSet.max(0)
ranges = maxVals - minVals
# 得到和训练集的同型矩阵
normDataSet = np.zeros(np.shape(dataSet))
# 得到行数
dataSetRowNum = dataSet.shape[0]
# 做矩阵数的减法和除法,实现归一化
normDataSet = dataSet - np.tile(minVals, (dataSetRowNum, 1))
normDataSet = normDataSet / np.tile(ranges, (dataSetRowNum, 1))
return normDataSet, ranges, minVals, dataSetRowNum
'''分类器测试'''
def datingClassTest(datingDataMat, datingLabels):
hoRatio = 0.1
# 归一化,得到归一矩阵、差值矩阵、最小值矩阵和行数
normMat, ranges, minVals, dataSetRowNum = autoNorm(datingDataMat)
# 取百分之10作为测试数据
numTestVecs = int(dataSetRowNum * hoRatio)
# 错误统计
errorCount = 0.0
for i in range(numTestVecs):
# 测试矩阵(0->numTestVecs-1), 训练矩阵(numTestVecs->dataSetRowNum),特征矩阵, K聚类的个数
classIfierResult = classIfY0(normMat[i, :], \
normMat[numTestVecs: dataSetRowNum, :], \
datingLabels[numTestVecs: dataSetRowNum], \
3)
print(f'the classifier came back with : {classIfierResult}, the real answer is : {datingLabels[i]}')
if classIfierResult != datingLabels[i]:
errorCount += 1
print(f'the total error rate is : {errorCount/float(numTestVecs)}')
pltFig(datingDataMat, datingLabels)
'''分类器个人'''
def datingClassPerson(datingDataMat, datingLabels):
resultList = ['不喜欢', '魅力一般', '极具魅力']
ffMiles = float(input('每年获得飞行常客里程数:'))
percentTats = float(input('玩视频游戏所耗时间:'))
iceCream = float(input('每周消费冰淇淋公升数:'))
# 创建单个小矩阵
inArr = np.array([ffMiles, percentTats, iceCream])
# 调用np.row_stack函数,将小矩阵并入大矩阵中
datingDataMat = np.row_stack((datingDataMat, inArr))
# 归一化,得到归一矩阵、差值矩阵、最小值矩阵和行数
normMat, ranges, minVals, dataSetRowNum = autoNorm(datingDataMat)
# 进入预测
classIfierResult = classIfY0(normMat[-1, :], normMat[:-1, :], datingLabels, 3)
print(resultList[int(classIfierResult) - 1])
if __name__ == '__main__':
num = input('1:测试KNN的正确性\n2:自行输入数据,预测约会网站的匹配结果\n')
mapData = r'G:\机器学习实战\机器学习实战资料\Ch02\datingTestSet2.txt'
# 读取文件
datingDataMat, datingLabels = file2matrix(mapData )
if num == '1':
datingClassTest(datingDataMat, datingLabels)
else:
datingClassPerson(datingDataMat, datingLabels)
简化了文件遍历的代码
和约会的代码差不多,核心还是小矩阵和大矩阵的匹配,找到最相近的匹配项目
import numpy as np
import operator
import os
'''读取文件内容到矩阵中'''
def img2vector(fileName):
# 创建1*1024的矩阵
returnVect = np.zeros((1, 1024))
with open(fileName) as fr:
# 共计32行
for i in range(32):
# 返回字符串
lineStr = fr.readline()
# 共计32列
for j in range(32):
# 将文件的数据写入到变量returnVect中,并转为整形
returnVect[0, 32*i+j] = int(lineStr[j])
return returnVect
'''inX为样本,dataSet为训练集,labels为标签向量,k为聚类个数'''
def classIfY0(inX, dataSet, labels, k):
# 返回dataSet训练集的行数;*shape[0]为行数、shape[1]为列数
dataSetSize = dataSet.shape[0]
# np.tile为拷贝,将 inX 拷贝行dataSetSize次,列1次
# 拷贝完的 inX' 减去dataSet矩阵
# 其实python具有广播机制也是可以的
# diffMat = inX - dataSet
diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet
# 做完减法后得平方
sqDiffMat = diffMat ** 2
# sqDiffMat矩阵按行相加,得到dataSetSize列矩阵;*axis=1按行相加、axis=0按列相加
sqDistances = sqDiffMat.sum(axis=1)
# 对sqDistances矩阵开方
distances = sqDistances**0.5
# 从小到大排列,提取其对应的index(索引)
sortedDistIndicies = distances.argsort()
# 创建空字典,用来记录K个邻居点的特征值名称(键)-数量(值)
classCount = {
}
for i in range(k):
# 找到[0, k)区间较小的值所对应的特征值
# sortedDistIndicies[i]返回数值较小的索引号
voteIlabel = labels[sortedDistIndicies[i]]
# 在字典中查找voteIlabel键的特征值个数
# 若找到则返回voteIlabel键下对应的值并加1;
# 若找不出,则新建voteIlabel(键)- 0(值)并加1
classCount[voteIlabel] = classCount.setdefault(voteIlabel, 0) + 1 #**
# classCount.items()返回键值对迭代器,字典形式:{'a':1,'b':3}-》列表形式[('a',1),('b',3)]
# key=operator.itemgetter(1)使用元素第二维的数据进行排序
# reverse=True决定了排序方式,从大到小
sortedClassCount = sorted(classCount.items(), key=operator.itemgetter(1), reverse=True) #**
return sortedClassCount[0][0]
def handwritingClassTest(trainingFolderMap, testFolderMap):
# 存放数字的特征值的特征值列表
hwLabels = []
# 读取训练列表
for fatherFolder, sonFolders, sonFiles in os.walk(trainingFolderMap):
# 创建 len(sonFiles)*1024矩阵
trainingMat = np.zeros((len(sonFiles), 1024))
# 统计次数,与trainingMat相对应
flagCount = 0
# 训练
for file in sonFiles:
# 将数字特征值写入特征值列表
hwLabels.append(file[0])
# 将文件内容小矩阵写入到大矩阵表中
trainingMat[flagCount, :] = img2vector(os.path.join(fatherFolder, file))
flagCount += 1
# 错误累计
errorCount = 0.0
# 读取预测列表
for fatherFolder, sonFolders, sonFiles in os.walk(testFolderMap):
for file in sonFiles:
# 读取数字的特征值
classNumStr = file[0]
# 读取文件的数据,生成1*1024的列表
vectorUnderTest = img2vector(os.path.join(fatherFolder, file))
# 将小矩阵的数据作为预测数据,大矩阵的数据作为训练数据,hwLabels为特征值
classIfierResult = classIfY0(vectorUnderTest, trainingMat, hwLabels, 3)
# 打印预测结果
print(f'the classifier came back with : {classIfierResult}, the real answer is : {classNumStr}')
# 错误统计
if classIfierResult != classNumStr:
errorCount += 1
print(f'the total error rate is : {errorCount/float(len(sonFiles))}')
trainingFolderMap = r'G:\机器学习实战\机器学习实战资料\Ch02\trainingDigits'
testFolderMap = r'G:\机器学习实战\机器学习实战资料\Ch02\testDigits'
print(handwritingClassTest(trainingFolderMap, testFolderMap))
测试文件资料:约会+手写