1 KNN算法的基本原理
KNN 算法其实简单的说就是“物以类聚”,也就是将新的没有被分类的点分类为周围的点中大多数属于的类。它采用测量不同特征值之间的距离方法进行分类,思想很简单:如果一个样本的特征空间中最为临近(欧式距离进行判断)的K个点大都属于某一个类,那么该样本就属于这个类。这就是物以类聚的思想。
当然,实际中,不同的K取值会影响到分类效果,并且在K个临近点的选择中,都不加意外的认为这K个点都是已经分类好的了,否则该算法也就失去了物以类聚的意义了。
KNN算法的不足点:
-
当样本不平衡时,比如一个类的样本容量很大,其他类的样本容量很小,输入一个样本的时候,K个临近值中大多数都是大样本容量的那个类,这时可能就会导致分类错误。改进方法是对K临近点进行加权,也就是距离近的点的权值大,距离远的点权值小。
-
计算量较大,每个待分类的样本都要计算它到全部点的距离,根据距离排序才能求得K个临近点,改进方法是:先对已知样本点进行剪辑,事先去除对分类作用不大的样本。
适用性:
适用于样本容量比较大的类域的自动分类,而样本容量较小的类域则容易误分
2 伪代码
- 步骤:
a.计算已知类别数据集中的点与当前点之间的距离;
b. 按照距离递增次序排序;
c.选取与当前点距离最小的k个点;
d.确定前k个点所在类别的出现频率;
e.返回前k个点出现频率最高的类别作为当前点的预测分类。
3 函数扫盲:
数组的大小可以通过其shape属性获得,shape[0]表示数组行数
numpy.tile(A,B):将A重复B次,这里B可以是int类型也可以是元组类型
tile([0,0],(3,1))在列方向上重复[0,0]1次行3次
sum()的参数是list,求和函数 axis=1行数为1
argsort()将数组按升序排列
{}字典类型
利用字典的get()方法从字典中获得一个值,且不需要处理字典里找不到你所需要的键值的异常。
get(voteLabel, 0)字典里没有就返回0
operator.itemgetter函数:
a = [1,2,3]
>>> b=operator.itemgetter(1) //定义函数b,获取对象的第1个域的值
>>> b(a)
2
>>> b=operator.itemgetter(1,0) //定义函数b,获取对象的第1个域和第0个的值
>>> b(a)
(2, 1)
sorted函数
Python内置的排序函数sorted可以对list或者iterator进行排序
该函数原型为:
sorted(iterable[, cmp[, key[, reverse]]])
参数解释:
(1)iterable指定要排序的list或者iterable,不用多说;
(2)cmp为函数,指定排序时进行比较的函数,可以指定一个函数或者lambda函数,如:
students为类对象的list,没个成员有三个域,用sorted进行比较时可以自己定cmp函数,例如这里要通过比较第三个数据成员来排序,代码可以这样写:
students = [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
sorted(students, key=lambda student : student[2])
(3)key为函数,指定取待排序元素的哪一项进行排序,函数用上面的例子来说明,代码如下:
sorted(students, key=lambda student : student[2])
key指定的lambda函数功能是去元素student的第三个域(即:student[2]),因此sorted排序时,会以students所有元素的第三个域来进行排序。
有了上面的operator.itemgetter函数,也可以用该函数来实现,例如要通过student的第三个域排序,可以这么写:
sorted(students, key=operator.itemgetter(2))
sorted函数也可以进行多级排序,例如要根据第二个域和第三个域进行排序,可以这么写:
sorted(students, key=operator.itemgetter(1,2))
即先根据第二个域排序,再根据第三个域排序。
(4)reverse参数就不用多说了,是一个bool变量,表示升序还是降序排列,默认为false(升序排列),定义为True时将按降序排列。
4 一个简单的待解决问题
已有数据:
group=array([[1.0,1.1],[1.0,1.0],[0,0],[0,0.1]])
labels=['a','a','b','b']
两组待分类数据:
[1.0,0.8]
[0.5,0.5]
求两组待分类数据分别属于哪一类
5 代码
from
numpy
import
*
import
operator
#已有数据,以及对应的标签
group
=
array([[
1.0
,
1.1
],[
1.0
,
1.0
],[
0
,
0
],[
0
,
0.1
]])
labels
=
[
'a'
,
'a'
,
'b'
,
'b'
]
'''''
作用:将待分类数据集与已有数据集以其标签进行计算,从而得出待分类数据集最有可能所属的类别
参数:
inX:待分类数据集
dataSet:已有数据集,通过createDataSet()函数获取
labels:已有数据集对应的分类标签,通过createDataSet()函数获取
k:设置最小距离数
'''
def
classify0(inX, dataSet, labels, k):
dataSetSize
=
dataSet.shape[
0
]
#获取数据集的行数,知道有n个dataSet
#计算距离
#下面这一行代码的结果是将待分类数据集扩展到与已有数据集同样的规模,然后再与已有数据集作差
diffMat
=
tile(inX, (dataSetSize,
1
))
-
dataSet
sqDiffMat
=
diffMat
*
*
2
#对上述差值求平方
sqDistances
=
sqDiffMat.
sum
(axis
=
1
)
#对于每一行数据求和
distances
=
sqDistances
*
*
0.5
#对上述结果开方
sortedDistIndicies
=
distances.argsort()
#对开方结果建立索引
#计算距离最小的k个点的Lable
classCount
=
{}
#建立空字典,类别字典,保存各类别的数目
for
i
in
range
(k):
#i在1-k范围内
voteIlabel
=
labels[sortedDistIndicies[i]]
#先找出开方结果中第i个值对应的Label值
classCount[voteIlabel]
=
classCount.get(voteIlabel,
0
)
+
1
# 存入当前label以及对应的类别值,字典中没有就返回0
#字典的get方法,查找classCount中是否包含voteIlabel,是则返回该 值,不是则返回defValue,这里是0
sortedClassCount
=
sorted
(classCount.items(), key
=
operator.itemgetter(
1
), reverse
=
True
)
#对类别字典第二个域进行逆排序
# 对字典中的类别出现次数进行排序,classCount中存储的事 key-value,其中key就是label,value就是出现的次数
# 所以key=operator.itemgetter(1)选中的事value,也就是对次数进行排序
#返回结果
return
sortedClassCount[
0
][
0
]
#返回级别字典中的第一个值,也就是最有可能的Label值
#进行分类
print
(classify0([
1.0
,
0.8
],group,labels,
3
))
print
(classify0([
0.5
,
0.5
],group,labels,
3
))
4 执行结果