准备:使用 Python 导入数据
"""
@函数说明: 创建数据集
"""
def createDataSet():
# 四组二维特征
group = np.array([[3,104],[2,100],[101,10],[99,5]])
# 四组特征的标签
labels = ['爱情片','爱情片','动作片','动作片']
return group, labels
"""
实施kNN算法
- 函数的伪代码如下:
对未知类别属性的数据集中的每个点依次执行以下操作:
(1) 计算已知类别数据集中的点与当前点之间的距离;
(2) 按照距离递增次序排序;
(3) 选取与当前点距离最小的k个点;
(4) 确定前k个点所在类别的出现频率;
(5) 返回前k个点出现频率最高的类别作为当前点的预测分类。
程序清单 2-1 k-近邻算法
"""
@函数说明:kNN分类(k近邻算法)
Parameters:
inX - 用于分类的数据(测试集)
dataSet - 用于训练的数据(训练集)
labels - 类别标签
k - 选择最近邻居的数目
Returns:
sortedClassCount[0][0] - 分类结果
"""
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0] # numpy函数shape[0]返回dataSet的行数,4行
# 欧式距离计算
# 横向复制inX共1次,纵向复制inX共dataSize次
# print(np.tile(inX, (dataSetSize,1)))
"""
构造相同行数的测试集test
[[ 10 120]
[ 10 120]
[ 10 120]
[ 10 120]]
"""
diffMat = np.tile(inX, (dataSetSize,1)) - dataSet # 沿横向、纵向复制多维数组
"""
test: group: diffMat:
[[ 10 120] [[ 3 104] [[ 7 16]
[ 10 120] —— [ 2 100] = [ 8 20]
[ 10 120] [101 10] [-91 110]
[ 10 120]] [ 99 5]] [-89 115]]
"""
# print(diffMat)
# 二维特征相减后平方
sqDiffMat = diffMat ** 2
# print(sqDiffMat)
"""
sqDiffMat(每个元素平方):
[[ 49 256]
[ 64 400]
[ 8281 12100]
[ 7921 13225]]
"""
# sum()将所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
# print("sqDistances:\n",sqDistances)
"""
sqDistances:
[ 305 464 20381 21146]
"""
# 开方,计算出距离
distances = sqDistances ** 0.5
# print(" distances:\n", distances)
"""
distances(算出来的最终的欧式距离):
[ 17.4642492 21.54065923 142.76203977 145.41664279]
"""
sortedDistIndices = distances.argsort() # 返回distances中元素从小到大排序后的索引值
# print(sortedDistIndices) # [0 1 2 3]
classCount = {} # 定义记录类别次数的字典
for i in range(k):
voteIlabel = labels[sortedDistIndices[i]] # 取出前K个元素的类别,前3个类别为:爱情片 爱情片 动作片
# print(voteIlabel)
#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
#计算类别次数
# print(classCount.get(voteIlabel,0))
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 # 没出现过的返回0再+1就是1次,爱情片(在空字典中没出现过,+1作为1次)、爱情片(出现过1次,+1是两次)、动作片(没出现过,+1作为1次)
# print(classCount[voteIlabel]) # 爱情片 爱情片 动作片, 1 2 1
# classCount: {'爱情片': 2, '动作片': 1}
"""
python内置函数:sorted函数
sorted(iterable<可迭代对象>, cmp<对函数排序>, key<对元素排序>, reverse<True降序,False升序(默认)>)
sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
详解:http://www.runoob.com/python/python-func-sorted.html
"""
#python3中用items()替换python2中的iteritems()
#key=operator.itemgetter(1)根据字典的值value进行排序
#key=operator.itemgetter(0)根据字典的键key进行排序
#reverse降序排序字典,reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
# print('sortedClassCount:\n',sortedClassCount)
"""
sortedClassCount:
[('爱情片', 2), ('动作片', 1)]
"""
return sortedClassCount[0][0]
示例:使用 k-近邻算法改进约会网站的配对效果
准备数据:从文本文件中解析数据
程序清单 2-2 将文本记录转换为NumPy的解析程序
"""
函数说明:打开并解析文件,对数据进行分类:1代表不喜欢、2代表魅力一般,3代表极具魅力
Parameters:
filename - 文件名
Return:
returnMat - 特征矩阵
classLabelVector - 分类Label向量
"""
def file2matrix(filename):
loveDict = {'didntLike':1,'smallDoses':2,'largeDoses':3}
fr = open(filename) # 打开文件
arrayOlines = fr.readlines() # 逐行读取
# print(arrayOlines)
'''
arrayOlines:
['40920\t8.326976\t0.953952\tlargeDoses\n', '14488\t7.153469\t1.673904\tsmallDoses\n',...,'43757\t7.882601\t1.332446\tlargeDoses\n']
'''
numberOfLines = len(arrayOlines) # 得到文件行数,共1000行数据
# print(numberOfLines)
returnMat = np.zeros( (numberOfLines,3) ) # 初始化特征矩阵,解析完成的数据:numberOfLines行3列
classLabelVector = [] # 初始化分类标签向量
index = 0 # 行的索引值
for line in arrayOlines:
# s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
line = line.strip() # 删去字符串首尾部空字符
# print(line)
'''
如最后两行line:
48111 9.134528 0.728045 largeDoses
43757 7.882601 1.332446 largeDoses
'''
# 使用s.split(str="",num=string,cout(str))将字符串根据'\t'分隔符进行切片
listFromLine = line.split('\t') # 按'\t'对字符串进行分割,listFromLine是列表
# print(listFromLine) # 分割成列表
'''
如最后两行listFromLine(列表):
['48111', '9.134528', '0.728045', 'largeDoses']
['43757', '7.882601', '1.332446', 'largeDoses']
'''
returnMat[index,:] = listFromLine[0:3] # 将数据前三列(特征)一行一行赋值给returnMat
index += 1
# 根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力
# 读取的listFromLine的最后一列为类别标签
if listFromLine[-1].isdigit(): # 如果listFromLine最后一列是数字,数据集datingTestSet.txt
classLabelVector.append(int(listFromLine[-1])) # 直接赋值给classLabelVector
else: # 如果listFromLine最后一列不是数字,而是字符串,数据集datingTestSet2.txt
classLabelVector.append(loveDict.get(listFromLine[-1]))
return returnMat, classLabelVector # 返回的类别标签向量是1,2,3
'''
returnMat: classLabelVector:
[[4.0920000e+04 8.3269760e+00 9.5395200e-01] [3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2,
[1.4488000e+04 7.1534690e+00 1.6739040e+00] 1, 1, 1, 1, 1, 2, 3, 2, 1, 2, 3, 2, 3,
[2.6052000e+04 1.4418710e+00 8.0512400e-01] ...
... 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2,
[2.6575000e+04 1.0650102e+01 8.6662700e-01] 2, 2, 2, 1, 3, 3, 3]
[4.8111000e+04 9.1345280e+00 7.2804500e-01]
[4.3757000e+04 7.8826010e+00 1.3324460e+00]]
'''
准备数据:归一化数值
程序清单 2-3 归一化特征值
"""
函数说明:特征归一化函数,对数据进行归一化
Parameters:
dataSet - 特征矩阵
Returns:
normDataSet - 归一化后的特征矩阵
ranges - 数据范围
minVals - 数据最小值
"""
def autoNorm(dataSet):
minVals = dataSet.min(0) # 获取数据每一列的最小值和最大值,返回一维列表,min(0) axis=0每一列,axis=1每一行
maxVals = dataSet.max(0)
ranges = maxVals - minVals # 最大值和最小值的范围
# print('minVals:\n',minVals) # [0. 0. 0.001156]
# print('maxVals:\n',maxVals) # [9.1273000e+04 2.0919349e+01 1.6955170e+00]
# print('ranges:\n',ranges) # [9.1273000e+04 2.0919349e+01 1.6943610e+00]
normDataSet = np.zeros(np.shape(dataSet)) # 初始化归一化特征矩阵,np.shape(dataSet)返回dataSet的矩阵行列数
# print(dataSet.shape) # (1000, 3)
m = dataSet.shape[0] # dataSet的行数,共1000行
'''
下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:
newValue = (oldValue - min) / (max - min)
其中min和max分别是数据集中的最小特征值和最大特征值。
'''
normDataSet = dataSet - np.tile(minVals, (m, 1)) # 原始值减去最小值
normDataSet = normDataSet / np.tile(ranges, (m, 1)) # 再除以最大值和最小值的差,得到归一化数据,normDataSet值被限定在[0, 1]之间
return normDataSet, ranges, minVals # 返回归一化数据结果,数据范围,最小值
'''
normDataSet:
[[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]]
ranges:
[9.1273000e+04 2.0919349e+01 1.6943610e+00]
minVals:
[0. 0. 0.001156]
'''
测试算法:作为完整程序验证分类器
程序清单 2-4 分类器针对约会网站的测试代码
"""
函数说明:分类器测试函数
"""
def datingClassTest():
hoRatio = 0.10 # 整个数据集的10%用来测试,即拿100个样本作为测试集
datingDataMat, datingLabels = file2matrix(r'D:\dataset\inaction\kNN\datingTestSet2.txt') # 导入数据集
normMat, ranges, minVals = autoNorm(datingDataMat) # 所有特征归一化,返回归一化特征矩阵,数据范围,最小值
# print(normMat.shape) # (1000, 3)
m = normMat.shape[0] # 样本个数,normMat的行数,共1000个样本
numTestVecs = int(m * hoRatio) # 测试样本个数,100个
errorCount = 0.0 # 分类错误计数
# print('测试集第一条数据:\n',normMat[0,:], '------------------------------','\n训练集:\n',normMat[numTestVecs:m,:])
'''
测试集第一条数据:
[0.44832535 0.39805139 0.56233353]
----------------------------------
训练集:
[[0.46457331 0.53983597 0.12206372]
[0.36871802 0.31502142 0.79791792]
[0.10047878 0.2252441 0.11391374]
...
[0.29115949 0.50910294 0.51079493]
[0.52711097 0.43665451 0.4290048 ]
[0.47940793 0.3768091 0.78571804]]
'''
for i in range(numTestVecs):
# 对测试集的每行数据进行分类测试
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:], datingLabels[numTestVecs:m], 3) # 取前numTestVecs个数据作为测试集,后m-numTestVecs个数据作为训练集,datingLabels[numTestVecs:m]为训练集标签
print("分类结果:%s, 真实类别:%d" % (classifierResult, datingLabels[i]))
if classifierResult != datingLabels[i]:
errorCount += 1.0
print("错误率:%g%%" % (errorCount / float(numTestVecs)*100)) # 打印错误率
print("错误个数:",errorCount)
使用算法:构建完整可用系统
程序清单 2-5 约会网站预测函数
"""
函数说明:通过用户输入一个人的三维特征,进行分类输出
"""
def classifyPerson():
resultList = ['一点也不喜欢','有点喜欢','非常喜欢']
# 用户输入特征
precentTats = float(input("玩视频游戏所消耗时间百分比:"))
ffMiles = float(input("每年获得的飞行常客里程数:"))
iceCream = float(input("每周消费的冰淇淋公升数:"))
datingDataMat, datingLabels = file2matrix(r'D:\dataset\inaction\kNN\datingTestSet2.txt') # 打开并处理数据集
normMat, ranges, minVals = autoNorm(datingDataMat) # 训练集归一化
# print('normMat:\n',normMat)
inArr = np.array([ffMiles, precentTats, iceCream]) # 测试集,生成Numpy数组
# print('inArr:\n',inArr)
norminArr = (inArr - minVals) / ranges # 测试集归一化
# print('norminArr:\n',norminArr)
'''
normMat:
[[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]]
inArr:
[1.34e+05 5.00e+01 9.00e-01]
norminArr:
[1.4681231 2.39013174 0.53049144]
'''
classifierResult = classify0(norminArr, normMat, datingLabels, 3) # 进行kNN分类
print("你可能%s这个人" % (resultList[classifierResult-1])) # 打印分类结果,classifierResult-1,数组下标从0起
示例:手写识别系统
准备数据:将图像转换为测试向量
"""
函数说明:将32*32的二进制图像转换成1*1024的向量
Parameters:
filename - 文件名(路径)
Returns:
returnVect - 返回的二进制图像的1*1024向量
"""
def img2vector(filename):
returnVect = np.zeros((1, 1024)) # 创建1*1024的零向量,存储图片像素的向量维度是1*1024
fr = open(filename) # 打开文件
for i in range(32): # 按行读取
lineStr = fr.readline() # 读一行数据
for j in range(32): # 每一行的前32个数据依次添加到returnVect中
returnVect[0, 32*i+j] = int(lineStr[j]) # 图片尺寸是32*32,将其依次放入向量returnVect
return returnVect # 返回转换后的1*1024向量
测试算法:使用 k-近邻算法识别手写数字
程序清单 2-6 手写数字识别系统的测试代码
"""
函数说明:手写数字分类测试
"""
def handwritingClassTest():
hwLabels = [] # 测试集Labels
trainingFileList = listdir(r'D:\dataset\inaction\kNN\trainingDigits') # 导入训练集,listdir() 返回指定文件夹名字的列表
m = len(trainingFileList) # 文件夹下文件的个数,1934个文件
# print(m)
trainingMat = np.zeros((m, 1024)) # 初始化训练矩阵
for i in range(m):
fileNameStr = trainingFileList[i] # 获得每个文件的名字,如 0_0.txt,9_99.txt
# print(fileNameStr)
fileStr = fileNameStr.split('.')[0] # 去掉 .txt , 剩下0_0,9_99
# print(fileStr)
classNumStr = int(fileStr.split('_')[0]) # 按下划线‘_’划分‘0_0’,取第一个元素为类别标签
hwLabels.append(classNumStr) # 将获得的类别添加到hwLabels中
# print(hwLabels)
trainingMat[i,:] = img2vector(r'D:\dataset\inaction\kNN\trainingDigits\%s' % fileNameStr) # 将每一个文件的1*1024数据存储到trainingMat矩阵中
testFileList = listdir(r'D:\dataset\inaction\kNN\testDigits') # 测试样本,返回testDigits目录下的文件名
errorCount = 0.0 # 分类错误计数
mTest = len(testFileList) # 测试样本的个数
for i in range(mTest):
fileNameStr = testFileList[i] # 获得每个文件的名字,如 0_0.txt
fileStr = fileNameStr.split('.')[0] # 去掉 .txt , 剩下0_0
classNumStr = int(fileStr.split('_')[0]) # 按下划线‘_’划分‘0_0’,取第一个元素为类别标签
vectorUnderTest = img2vector(r'D:\dataset\inaction\kNN\testDigits\%s' % fileNameStr) # 获得测试集的1*1024向量,用于训练
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) # 调用kNN分类,获得预测结果
print("分类返回结果:%d, 真实结果:%d" % (classifierResult, classNumStr)) # 打印分类结果
if classifierResult != classNumStr:
errorCount += 1.0 # 分类错误个数计数
print("错误总数:%d\n错误率:%f%%" % (errorCount, errorCount / float(mTest) * 100)) #打印错误个数及错误率
附所有代码:
# -*- coding: utf-8 -*-
"""
Created on Tue Oct 9 09:56:29 2018
@author: YAOTIANLONG
"""
# ------示例1: kNN电影分类 -------------------------------------
import numpy as np
import operator # 导入运算符模块,sorted排序使用,key=operator.itemgetter(1)
from os import listdir # os.listdir() 方法用于返回指定的文件夹包含的文件或文件夹的名字的列表
"""
@函数说明: 创建数据集
"""
def createDataSet():
# 四组二维特征
group = np.array([[3,104],[2,100],[101,10],[99,5]])
# 四组特征的标签
labels = ['爱情片','爱情片','动作片','动作片']
return group, labels
"""
@函数说明:kNN分类(k近邻算法)
Parameters:
inX - 用于分类的数据(测试集)
dataSet - 用于训练的数据(训练集)
labels - 类别标签
k - 选择最近邻居的数目
Returns:
sortedClassCount[0][0] - 分类结果
"""
def classify0(inX, dataSet, labels, k):
dataSetSize = dataSet.shape[0] # numpy函数shape[0]返回dataSet的行数,4行
# 欧式距离计算
# 横向复制inX共1次,纵向复制inX共dataSize次
# print(np.tile(inX, (dataSetSize,1)))
"""
构造相同行数的测试集test
[[ 10 120]
[ 10 120]
[ 10 120]
[ 10 120]]
"""
diffMat = np.tile(inX, (dataSetSize,1)) - dataSet # 沿横向、纵向复制多维数组
"""
test: group: diffMat:
[[ 10 120] [[ 3 104] [[ 7 16]
[ 10 120] —— [ 2 100] = [ 8 20]
[ 10 120] [101 10] [-91 110]
[ 10 120]] [ 99 5]] [-89 115]]
"""
# print(diffMat)
# 二维特征相减后平方
sqDiffMat = diffMat ** 2
# print(sqDiffMat)
"""
sqDiffMat(每个元素平方):
[[ 49 256]
[ 64 400]
[ 8281 12100]
[ 7921 13225]]
"""
# sum()将所有元素相加,sum(0)列相加,sum(1)行相加
sqDistances = sqDiffMat.sum(axis=1)
# print("sqDistances:\n",sqDistances)
"""
sqDistances:
[ 305 464 20381 21146]
"""
# 开方,计算出距离
distances = sqDistances ** 0.5
# print(" distances:\n", distances)
"""
distances(算出来的最终的欧式距离):
[ 17.4642492 21.54065923 142.76203977 145.41664279]
"""
sortedDistIndices = distances.argsort() # 返回distances中元素从小到大排序后的索引值
# print(sortedDistIndices) # [0 1 2 3]
classCount = {} # 定义记录类别次数的字典
for i in range(k):
voteIlabel = labels[sortedDistIndices[i]] # 取出前K个元素的类别,前3个类别为:爱情片 爱情片 动作片
# print(voteIlabel)
#dict.get(key,default=None),字典的get()方法,返回指定键的值,如果值不在字典中返回默认值。
#计算类别次数
# print(classCount.get(voteIlabel,0))
classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1 # 没出现过的返回0再+1就是1次,爱情片(在空字典中没出现过,+1作为1次)、爱情片(出现过1次,+1是两次)、动作片(没出现过,+1作为1次)
# print(classCount[voteIlabel]) # 爱情片 爱情片 动作片, 1 2 1
# classCount: {'爱情片': 2, '动作片': 1}
"""
python内置函数:sorted函数
sorted(iterable<可迭代对象>, cmp<对函数排序>, key<对元素排序>, reverse<True降序,False升序(默认)>)
sort 是应用在 list 上的方法,sorted 可以对所有可迭代的对象进行排序操作。
内建函数 sorted 方法返回的是一个新的 list,而不是在原来的基础上进行的操作。
详解:http://www.runoob.com/python/python-func-sorted.html
"""
#python3中用items()替换python2中的iteritems()
#key=operator.itemgetter(1)根据字典的值value进行排序
#key=operator.itemgetter(0)根据字典的键key进行排序
#reverse降序排序字典,reverse -- 排序规则,reverse = True 降序 , reverse = False 升序(默认)
sortedClassCount = sorted(classCount.items(),key=operator.itemgetter(1),reverse=True)
# print('sortedClassCount:\n',sortedClassCount)
"""
sortedClassCount:
[('爱情片', 2), ('动作片', 1)]
"""
return sortedClassCount[0][0]
"""
函数说明:测试kNN电影分类
"""
def test0():
group, labels = createDataSet() # 创建数据集
print('group:\n',group) # 打印数据集
print('labels:\n',labels)
test = [101,20] # 测试集
test_class = classify0(test, group, labels, 3) # kNN分类
print(test_class)
print('"',test,'kNN classify',test_class,'"') # 打印分类结果
# ------示例2: 使用kNN算法改进约会网站的配对效果 -------------------------------------
"""
函数说明:打开并解析文件,对数据进行分类:1代表不喜欢、2代表魅力一般,3代表极具魅力
Parameters:
filename - 文件名
Return:
returnMat - 特征矩阵
classLabelVector - 分类Label向量
"""
def file2matrix(filename):
loveDict = {'didntLike':1,'smallDoses':2,'largeDoses':3}
fr = open(filename) # 打开文件
arrayOlines = fr.readlines() # 逐行读取
# print(arrayOlines)
'''
arrayOlines:
['40920\t8.326976\t0.953952\tlargeDoses\n', '14488\t7.153469\t1.673904\tsmallDoses\n',...,'43757\t7.882601\t1.332446\tlargeDoses\n']
'''
numberOfLines = len(arrayOlines) # 得到文件行数,共1000行数据
# print(numberOfLines)
returnMat = np.zeros( (numberOfLines,3) ) # 初始化特征矩阵,解析完成的数据:numberOfLines行3列
classLabelVector = [] # 初始化分类标签向量
index = 0 # 行的索引值
for line in arrayOlines:
# s.strip(rm),当rm空时,默认删除空白符(包括'\n','\r','\t',' ')
line = line.strip() # 删去字符串首尾部空字符
# print(line)
'''
如最后两行line:
48111 9.134528 0.728045 largeDoses
43757 7.882601 1.332446 largeDoses
'''
# 使用s.split(str="",num=string,cout(str))将字符串根据'\t'分隔符进行切片
listFromLine = line.split('\t') # 按'\t'对字符串进行分割,listFromLine是列表
# print(listFromLine) # 分割成列表
'''
如最后两行listFromLine(列表):
['48111', '9.134528', '0.728045', 'largeDoses']
['43757', '7.882601', '1.332446', 'largeDoses']
'''
returnMat[index,:] = listFromLine[0:3] # 将数据前三列(特征)一行一行赋值给returnMat
index += 1
# 根据文本中标记的喜欢的程度进行分类,1代表不喜欢,2代表魅力一般,3代表极具魅力
# 读取的listFromLine的最后一列为类别标签
if listFromLine[-1].isdigit(): # 如果listFromLine最后一列是数字,数据集datingTestSet.txt
classLabelVector.append(int(listFromLine[-1])) # 直接赋值给classLabelVector
else: # 如果listFromLine最后一列不是数字,而是字符串,数据集datingTestSet2.txt
classLabelVector.append(loveDict.get(listFromLine[-1]))
return returnMat, classLabelVector # 返回的类别标签向量是1,2,3
'''
returnMat: classLabelVector:
[[4.0920000e+04 8.3269760e+00 9.5395200e-01] [3, 2, 1, 1, 1, 1, 3, 3, 1, 3, 1, 1, 2,
[1.4488000e+04 7.1534690e+00 1.6739040e+00] 1, 1, 1, 1, 1, 2, 3, 2, 1, 2, 3, 2, 3,
[2.6052000e+04 1.4418710e+00 8.0512400e-01] ...
... 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 2, 2,
[2.6575000e+04 1.0650102e+01 8.6662700e-01] 2, 2, 2, 1, 3, 3, 3]
[4.8111000e+04 9.1345280e+00 7.2804500e-01]
[4.3757000e+04 7.8826010e+00 1.3324460e+00]]
'''
"""
函数说明:特征归一化函数,对数据进行归一化
Parameters:
dataSet - 特征矩阵
Returns:
normDataSet - 归一化后的特征矩阵
ranges - 数据范围
minVals - 数据最小值
"""
def autoNorm(dataSet):
minVals = dataSet.min(0) # 获取数据每一列的最小值和最大值,返回一维列表,min(0) axis=0每一列,axis=1每一行
maxVals = dataSet.max(0)
ranges = maxVals - minVals # 最大值和最小值的范围
# print('minVals:\n',minVals) # [0. 0. 0.001156]
# print('maxVals:\n',maxVals) # [9.1273000e+04 2.0919349e+01 1.6955170e+00]
# print('ranges:\n',ranges) # [9.1273000e+04 2.0919349e+01 1.6943610e+00]
normDataSet = np.zeros(np.shape(dataSet)) # 初始化归一化特征矩阵,np.shape(dataSet)返回dataSet的矩阵行列数
# print(dataSet.shape) # (1000, 3)
m = dataSet.shape[0] # dataSet的行数,共1000行
'''
下面的公式可以将任意取值范围的特征值转化为0到1区间内的值:
newValue = (oldValue - min) / (max - min)
其中min和max分别是数据集中的最小特征值和最大特征值。
'''
normDataSet = dataSet - np.tile(minVals, (m, 1)) # 原始值减去最小值
normDataSet = normDataSet / np.tile(ranges, (m, 1)) # 再除以最大值和最小值的差,得到归一化数据,normDataSet值被限定在[0, 1]之间
return normDataSet, ranges, minVals # 返回归一化数据结果,数据范围,最小值
'''
normDataSet:
[[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]]
ranges:
[9.1273000e+04 2.0919349e+01 1.6943610e+00]
minVals:
[0. 0. 0.001156]
'''
"""
函数说明:分类器测试函数
"""
def datingClassTest():
hoRatio = 0.10 # 整个数据集的10%用来测试,即拿100个样本作为测试集
datingDataMat, datingLabels = file2matrix(r'D:\dataset\inaction\kNN\datingTestSet2.txt') # 导入数据集
normMat, ranges, minVals = autoNorm(datingDataMat) # 所有特征归一化,返回归一化特征矩阵,数据范围,最小值
# print(normMat.shape) # (1000, 3)
m = normMat.shape[0] # 样本个数,normMat的行数,共1000个样本
numTestVecs = int(m * hoRatio) # 测试样本个数,100个
errorCount = 0.0 # 分类错误计数
# print('测试集第一条数据:\n',normMat[0,:], '------------------------------','\n训练集:\n',normMat[numTestVecs:m,:])
'''
测试集第一条数据:
[0.44832535 0.39805139 0.56233353]
----------------------------------
训练集:
[[0.46457331 0.53983597 0.12206372]
[0.36871802 0.31502142 0.79791792]
[0.10047878 0.2252441 0.11391374]
...
[0.29115949 0.50910294 0.51079493]
[0.52711097 0.43665451 0.4290048 ]
[0.47940793 0.3768091 0.78571804]]
'''
for i in range(numTestVecs):
# 对测试集的每行数据进行分类测试
classifierResult = classify0(normMat[i,:], normMat[numTestVecs:m,:], datingLabels[numTestVecs:m], 3) # 取前numTestVecs个数据作为测试集,后m-numTestVecs个数据作为训练集,datingLabels[numTestVecs:m]为训练集标签
print("分类结果:%s, 真实类别:%d" % (classifierResult, datingLabels[i]))
if classifierResult != datingLabels[i]:
errorCount += 1.0
print("错误率:%g%%" % (errorCount / float(numTestVecs)*100)) # 打印错误率
print("错误个数:",errorCount)
"""
函数说明:通过用户输入一个人的三维特征,进行分类输出
"""
def classifyPerson():
resultList = ['一点也不喜欢','有点喜欢','非常喜欢']
# 用户输入特征
precentTats = float(input("玩视频游戏所消耗时间百分比:"))
ffMiles = float(input("每年获得的飞行常客里程数:"))
iceCream = float(input("每周消费的冰淇淋公升数:"))
datingDataMat, datingLabels = file2matrix(r'D:\dataset\inaction\kNN\datingTestSet2.txt') # 打开并处理数据集
normMat, ranges, minVals = autoNorm(datingDataMat) # 训练集归一化
# print('normMat:\n',normMat)
inArr = np.array([ffMiles, precentTats, iceCream]) # 测试集,生成Numpy数组
# print('inArr:\n',inArr)
norminArr = (inArr - minVals) / ranges # 测试集归一化
# print('norminArr:\n',norminArr)
'''
normMat:
[[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]]
inArr:
[1.34e+05 5.00e+01 9.00e-01]
norminArr:
[1.4681231 2.39013174 0.53049144]
'''
classifierResult = classify0(norminArr, normMat, datingLabels, 3) # 进行kNN分类
print("你可能%s这个人" % (resultList[classifierResult-1])) # 打印分类结果,classifierResult-1,数组下标从0起
"""
函数说明:测试约会网站配对
"""
def test1():
datingDataMat, datingLabels = file2matrix(r'D:\dataset\inaction\kNN\datingTestSet2.txt') # 打开数据
print('datingDataMat:\n',datingDataMat) # 打印特征矩阵
print('datingLabels:\n',datingLabels) # 打印类别标签
normDataSet, ranges, minVals = autoNorm(datingDataMat) # 测试归一化特征函数
print('normDataSet:\n',normDataSet) # 打印归一化特征矩阵
print('ranges:\n',ranges) # 打印数据范围
print('minVals:\n',minVals) # 打印数据最小值
datingClassTest() # 测试分类器
classifyPerson() # 测试完整可用系统
# ------示例3: 手写识别系统 -------------------------------------
"""
函数说明:将32*32的二进制图像转换成1*1024的向量
Parameters:
filename - 文件名(路径)
Returns:
returnVect - 返回的二进制图像的1*1024向量
"""
def img2vector(filename):
returnVect = np.zeros((1, 1024)) # 创建1*1024的零向量,存储图片像素的向量维度是1*1024
fr = open(filename) # 打开文件
for i in range(32): # 按行读取
lineStr = fr.readline() # 读一行数据
for j in range(32): # 每一行的前32个数据依次添加到returnVect中
returnVect[0, 32*i+j] = int(lineStr[j]) # 图片尺寸是32*32,将其依次放入向量returnVect
return returnVect # 返回转换后的1*1024向量
"""
函数说明:手写数字分类测试
"""
def handwritingClassTest():
hwLabels = [] # 测试集Labels
trainingFileList = listdir(r'D:\dataset\inaction\kNN\trainingDigits') # 导入训练集,listdir() 返回指定文件夹名字的列表
m = len(trainingFileList) # 文件夹下文件的个数,1934个文件
# print(m)
trainingMat = np.zeros((m, 1024)) # 初始化训练矩阵
for i in range(m):
fileNameStr = trainingFileList[i] # 获得每个文件的名字,如 0_0.txt,9_99.txt
# print(fileNameStr)
fileStr = fileNameStr.split('.')[0] # 去掉 .txt , 剩下0_0,9_99
# print(fileStr)
classNumStr = int(fileStr.split('_')[0]) # 按下划线‘_’划分‘0_0’,取第一个元素为类别标签
hwLabels.append(classNumStr) # 将获得的类别添加到hwLabels中
# print(hwLabels)
trainingMat[i,:] = img2vector(r'D:\dataset\inaction\kNN\trainingDigits\%s' % fileNameStr) # 将每一个文件的1*1024数据存储到trainingMat矩阵中
testFileList = listdir(r'D:\dataset\inaction\kNN\testDigits') # 测试样本,返回testDigits目录下的文件名
errorCount = 0.0 # 分类错误计数
mTest = len(testFileList) # 测试样本的个数
for i in range(mTest):
fileNameStr = testFileList[i] # 获得每个文件的名字,如 0_0.txt
fileStr = fileNameStr.split('.')[0] # 去掉 .txt , 剩下0_0
classNumStr = int(fileStr.split('_')[0]) # 按下划线‘_’划分‘0_0’,取第一个元素为类别标签
vectorUnderTest = img2vector(r'D:\dataset\inaction\kNN\testDigits\%s' % fileNameStr) # 获得测试集的1*1024向量,用于训练
classifierResult = classify0(vectorUnderTest, trainingMat, hwLabels, 3) # 调用kNN分类,获得预测结果
print("分类返回结果:%d, 真实结果:%d" % (classifierResult, classNumStr)) # 打印分类结果
if classifierResult != classNumStr:
errorCount += 1.0 # 分类错误个数计数
print("错误总数:%d\n错误率:%f%%" % (errorCount, errorCount / float(mTest) * 100)) #打印错误个数及错误率
"""
函数说明:测试手写识别系统
"""
def test2():
testVector = img2vector(r'D:\dataset\inaction\kNN\testDigits\0_13.txt')
print(testVector[0,0:31])
print(testVector[0,32:63])
handwritingClassTest()
#-----------主函数---------------------------------------------
if __name__ == '__main__':
test0() # 测试kNN电影分类
test1() # 测试约会网站配对
test2() # 测试手写识别系统
附所有执行结果: