这里的代码是基于Python3的
这里先上详解
最后附上完整代码注释
我的博客素:https://www.cnblogs.com/EvilAnne/
如果我比较勤劳的话,会更新完整本书
否则,我会放弃!
![](https://i-blog.csdnimg.cn/blog_migrate/41d3859e1f35ddb4ef7336d1cbdb11d6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9d48c15fbf339aeac3903e0f9381e3cf.png)
①dataSetSize = dataSet.shape[0]
是来计算共有多少数据集,如是([[1,3],[3,4]]),就是两组数据集,相当于
| x1 | x2 |
| 1 | 3 |
| 3 | 4 |
dataSetSize = dataSet.shape[0] = 2
②diffMat = tile(inX,(dataSetSize,1)) - dataSet
这是来计算,未知类的数据集与已知数据集的差,但为了方便
要把未知类的数据集化成矩阵计算,设未知类数据是([[0,3]])
即 | x1' | x2' |
| 0 | 3 |
我们想计算 0-1,3-3;0-3,3-4
这样可以更方便
| 0 3 | - | 1 3 | = | -1 0 | (-dataSet计算差值)
| 0 3 | | 3 4 | | -3 -1 |
而diffMat = tile(inX,(dataSetSize,1))就是
把[0,3] - > | 0 3 | 来方便计算的
| 0 3 |
③sqDiffMat = diffMat ** 2
把差值平方化即
| -1 0 | ** 2 = | 1 0 |
| -3 -1 | | 9 1 |
④distances = sqDiffMat.sum(axis=1)
相当于把(未平方根化之前的)未知数据集与两个已知数据的距离分别计算出来
得出[1,10],1是未知与已知①的距离,10是未知与已知②的距离
⑤distances = sqdistances ** 0.5
将两个距离分别进行平方根化,得到
该未知标签向量与已知①的欧式距离,该未知标签向量与已知②的欧式距离
⑥sortedDistIndices = distances.argsort()
比如说,
[3,5,1] ---> [2,0,1] 从小到大的索引
从小到大分别是:1,3,5.对应的索引是2,0,1
这样我们在循环的时候,可以将欧式距离list按值的大小从小到大遍历出来
------------------------------------------------------------------------------------
![](https://i-blog.csdnimg.cn/blog_migrate/2fecb68b044a75ddf70bf1eae9ad6a30.png)
把按值大小顺序排列的欧氏距离索引list前k个对应的labels遍历出来
⑨classCount[voteIlabel] = classCount.get(voteIlabel,0) + 1
比如说,k = {};k['A'] = k.get('A',1);k = {'A':1}
而 k = {};k['A'] = k.get('A',1) + 1;k = {'A':2}
但我不太明白这里为什么要+1
所以我们来实验一下⑨到底是什么意思
for i in range(k): #k是我们自己定的
voteIlabel = labels[sortedDistIndicies[i]] #把按值大小顺序排列的欧氏距离索引list前k个对应的labels遍历出来
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
假设labels = ['A','A','B','B']
假设sortedDistIndicies = [0,3,2,1]
令k = 4
---------
当k = 0,
voteIlabel = labels[sortedDistIndicies[0]]
= labels[0]
= 'A'
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
↓
classCount['A'] = classCount.get('A', 0) + 1
所以有classCount = {'A':1}
---------
k = 1
voteIlabel = labels[sortedDistIndicies[1]]
= labels[1]
= 'A'
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
↓
classCount['A'] = classCount.get('A', 0) + 1
所以有classCount = {'A':2}
---------
k = 2
voteIlabel = labels[sortedDistIndicies[2]]
= labels[2]
= 'B'
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
↓
classCount['B'] = classCount.get('B', 0) + 1
所以有classCount = {'A':2,'B':1}
---------
k = 3
voteIlabel = labels[sortedDistIndicies[3]]
= labels[3]
= 'B'
classCount[voteIlabel] = classCount.get(voteIlabel, 0) + 1
↓
classCount['B'] = classCount.get('B', 0) + 1
所以有classCount = {'A':2,'B':2}
---------
可以看出,整合的是分类所出现的频数
所以⑨应该是整合的是,按值大小顺序排列的欧氏距离list前k个对应的labels的频数
![](https://i-blog.csdnimg.cn/blog_migrate/5e7fcaa25e529f5bd484b32b055eba0a.png)
假设classCount = {“A”:2,"B":2}
classCount.items()返回的是dict_items
![](https://i-blog.csdnimg.cn/blog_migrate/8d1337f21256534024382410e17cbb97.png)
operator.itemgetter()返回的是一个函数
operator.itemgetter(1)按照第二个元素的次序对元组进行排序,reverse=True是逆序,即按照从大到小的顺序排列
意思是取dict_items第1个值(从0开始)
所以 sorted这里的意思是:
classCount.items()将classCount字典分解为元组列表
即由
变成
![](https://i-blog.csdnimg.cn/blog_migrate/539bd38b124e8f64b17f186f7eece9bc.png)
![](https://i-blog.csdnimg.cn/blog_migrate/4e75b9514fc4d9ecd32913da7293da7e.png)
并且按第二个元素进行从大到小的排列例子如下
因为我这里都是2,所以不明显,大家自己可以试试把数字改一下
![](https://i-blog.csdnimg.cn/blog_migrate/e4c90e77fcf4a2422d90c58ccd6abbf6.png)
最后return sortedClassCount[0][0]
容易理,就是出解现频数最高的那个对应的分类标签
file2matrix数据预处理函数
string.strip([chars])
strip: 用来去除头尾字符、空白符(包括\n、\r、\t、' ',即:换行、回车、制表符、空格)
参数chars是可选的,当chars为空,默认删除string头尾的空白符(包括\n、\r、\t、' ')
split([char])
split():拆分字符串。通过指定分隔符对字符串进行切片,并返回分割后的字符串列表(list)
不理解
的意思
![](https://i-blog.csdnimg.cn/blog_migrate/9e87683f0541c48f42ddb894ca00197a.png)
先不百度,自己实验一下
可以知道returnMat[index,:] ,这里index = 0,就是取该数据的第一行数据,由于初始化为零矩阵
所以是[0,0,0]
![](https://i-blog.csdnimg.cn/blog_migrate/a1ae3612db1a1068565923e21ec8fd98.png)
对于
通过实验可以知道取前三个数据
![](https://i-blog.csdnimg.cn/blog_migrate/bfeab246cd8f80eedb91fad0e2111787.png)
也就是说读取特征向量
所以
的意思就是
![](https://i-blog.csdnimg.cn/blog_migrate/380846e58863f0e4a2bbe588da63fbab.png)
取这个文件的特征向量
![](https://i-blog.csdnimg.cn/blog_migrate/7c9bc25bff4f12f11b8e4a04263be6fb.png)
如果字符串只包含数字则返回 True 否则返回 False。
img2vector
![](https://i-blog.csdnimg.cn/blog_migrate/b0bec379e374ed22285b2980eac2e49f.png)
![](https://i-blog.csdnimg.cn/blog_migrate/9e6d12c2508073429c3e93ac410283b1.png)
这句不理解,自己实验下,可知道是取第0行的第(32*i+j)个数据
我们知道returnVect是 1 * 1024的零矩阵
![](https://i-blog.csdnimg.cn/blog_migrate/b39695627aa549656467160269e9cca5.png)
![](https://i-blog.csdnimg.cn/blog_migrate/7e70e18bd35848c986a05f63b8f06f1a.png)
linestr是遍历读取的每行数据
所以整体的意思就是说把原来32 * 32的数据
转换成1 * (32*32) = 1 * 1024 的数据
如
这个4 * 3的向量
![](https://i-blog.csdnimg.cn/blog_migrate/7243299f3b99e33a3a9c3673ad25bdb5.png)
我把它转换成1 * (4*3) = 1 * 12的向量
就是[1,2,3,4,5,6,7,8,9,9,9,9]
handwritingClassTest函数
解释下以下代码
其实看test就能很好理解了
![](https://i-blog.csdnimg.cn/blog_migrate/9393497cb25696d921c7505ae569563c.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c623d67d5d3c6d7e8c1723d6eee38793.png)
完整代码及注释:
![](https://i-blog.csdnimg.cn/blog_migrate/5c56f6b5a92d949a0a34ff173c8a2cf8.png)