1 分类:python实现kNN / k-邻近点算法


很多通俗语言描述,不严谨,请见谅。
详细理论有机会再补充。

未完待续…

  • kNN不需要确定 模型训练.

本节使用到的python与numpy函数

  • 属性 dataSet.shape

dataSet是一个numpy array 数据集,dataSet.shape返回元组,元组每个位置表示对应维度的大小。

a = np.array([[[1.0, 1.1], [1.1, 1.0], [0, 0], [0, 0.1]]])
print(a)
print(a.shape)
[[[1.  1.1]
  [1.1 1. ]
  [0.  0. ]
  [0.  0.1]]]
(1, 4, 2)
  • 函数 np.tile(array,(m,n))

tile就是瓷砖的意思,顾名思义,就是将array重复m行n列。
注意: 当第二个变量只是一个数时,默认横向复制。

arr = np.array([1, 2])
arr1 = np.tile(arr, 3)
arr2 = np.tile(arr, (1, 3))
arr3 = np.tile(arr, (3, 2))
[1 2 1 2 1 2]

[[1 2 1 2 1 2]]

[[1 2 1 2]
 [1 2 1 2]
 [1 2 1 2]]
  • 方法 arr.sum(axis = n)

arr 是一个numpy array,该方法表示对arr的第n个维度求和,求和后返回的array会小一个维度。
按照第一个维度求和举例: ( a ) i j k (a)_{ijk} (a)ijk是一个 ( m , n , l ) (m,n,l) (m,n,l)数组,对每一对 ( j , k ) (j,k) (j,k),在第一维上求和
∑ i m ( a i , j , k ) \sum_{i}^m(a_{i,j,k}) im(ai,j,k)
简单来说,对第一维求和就是其他两个维度位置固定,按第一维的下标进行求和。
numpy数组最里面的括号维度最小,最外面的括号维度最大。

a = np.array([[[1.0, 1.1], [1.1, 1.0], [0, 0], [0, 0.1]]])
print(a)
print('sum:', a.sum(axis=1), sep='\n')
[[[1.  1.1]
  [1.1 1. ]
  [0.  0. ]
  [0.  0.1]]]
sum:
[[2.1 2.2]]
  • 方法 arr.argsort() 或 函数np.argsort()

看到sort说明这个方法关于排序,看到arg说明这个方法可能返回的时排序的index。
np.argsort(a, axis=-1, kind=‘quicksort’, order=None)
函数功能: 将a中的元素从小到大排列,提取其在排列前对应的index(索引)输出。
作用: 说明按照argsort给的index顺序对原有的数组进行索引,会得到原数组的递增序列。

rand_arr = np.random.randint(1, 10, 10)
print("original:", rand_arr, sep='\n')
sorted_indx = rand_arr.argsort()
print("sorted index:", sorted_indx, sep='\n')
# 用sorted_indx提取ran_arr最小的5个元素
print("extract:")
[print(rand_arr[sorted_indx[i]],end='  ') for i in range(5)]
original:
[6 4 2 1 4 4 1 4 3 2]
sorted index:
[3 6 2 9 8 1 4 5 7 0]
extract:
1  1  2  2  3  
  • 字典方法dict.get(key[,value])

key为指定键值
value是可选参数,若指定键值不存在时则返回value或者None。
注意: 如果字典dict已经有key对应的键值对,那么不管给不给value传参,都返回原value。如果没有key这个键,并且不给value传参,则返回None。如果没有key这个键,给value传参,就会创建key:value这个键值对,并且返回这个刚传的value。

mydict={'name':'naruto','age':24}
print("age:%s" % mydict.get('age'))
print("sex:%s" % mydict.get('sex'))
print('salary:%s' % mydict.get('salary',-999))

age:24
sex:None
salary:-999

该例子来自:https://blog.csdn.net/qq_40061206/article/details/124979462

  • 函数 operator.itemgetter()

operator是一个模块,operator.itemgetter()返回的是一个callable对象/函数对象,功能和索引的功能很像。给这个对象传入一个可迭代对象,比如列表,返回指定索引下的值。

tup1 = (1, 2, 3, 4, 5, 6)
inx2_getter = operator.itemgetter(2) # 提取可迭代对象的第3个元素
print(inx2_getter(tup1))
3

作用: 经常与sorted()函数一起用。
sorted(iterable,key,reverse),key参数可传入一个自定义函数,可以是operator.itemgetter(2),意思就是以第三个元素的值为基准,进行排序。
经常用于 元组列表字典 的排序。

students = [('james', 12, 'm'), ('tom', 13, 'm'), ('lucy', 10, 'f')]
sorted_stus = sorted(students, key=operator.itemgetter(1), reverse=True)
print(sorted_stus)
[('tom', 13, 'm'), ('james', 12, 'm'), ('lucy', 10, 'f')]

再看看sorted的另一个例子吧。

tup_list = [(1,), (1, 2), (1, 2, 3), (1, 2, 3, 4)]
sorted_list = sorted(tup_list, key=len, reverse=True)
print(sorted_list)
[(1, 2, 3, 4), (1, 2, 3), (1, 2), (1,)]

利用python实现kNN算法

伪代码

输入:数据集,数据集的已知类别,k,要分类的点

  1. 计算已知类别数据集中的点和要分类的点之间的距离;
  2. 将距离递增排序;
  3. 选取距离最小的k个点;
  4. 确定这k个点对应类别出现的频率;
  5. 返回频率最高的类别。

classify0()函数

自己写的
def classify0(inX, dataSet, labels, k):
    """
    :kNN分类函数
    :param inX: 需要分类的向量
    :param dataSet: 训练样本集
    :param labels: 与训练集对应的标签向量
    :param k: 选择最邻近的数目
    :return: inX的类型
    这里dataSet的数据类型肯定是numpy array,labels的数据类型可以是python list也可以是np array"""

    # 以下代码块计算距离
    # 这里距离用欧式距离,即两点分量差的平方和开根号
    dataSetSize = dataSet.shape[0]
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet  # diffMat存储每一个样本数据(向量)和inX(向量)的差
    sqDiffMat = diffMat ** 2  # 每一个分量的平方
    distances = sqDiffMat.sum(axis=1) ** 0.5  # 求行和然后每个分量开根号
    # 以下代码块将距离排序,选择距离最小的k个点
    sortedIndicies = distances.argsort()
    label_list = [labels[sortedIndicies[i]] for i in range(k)]  # 提取距离最小的k个数据点的label
    class_count = {}
    for x in label_list:
        class_count[x] = class_count.get(x, 0) + 1  # 字典存储label出现的频率
    sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)  # 将频率排序
    print(sorted_class_count)
    return sorted_class_count[0][0]
书上的
def classify0(inX, dataSet, labels, k):
    # 以下代码块计算距离
    # 这里距离用欧式距离,即两点分量差的平方和开根号
    dataSetSize = dataSet.shape[0]
    diffMat = np.tile(inX, (dataSetSize, 1)) - dataSet  # diffMat存储每一个样本数据(向量)和inX(向量)的差
    sqDiffMat = diffMat ** 2  # 每一个分量的平方
    distances = sqDiffMat.sum(axis=1) ** 0.5  # 求行和然后每个分量开根号
    # 以下代码块将距离排序,选择距离最小的k个点
    sortedIndicies = distances.argsort()
    class_count = {}
    for i in range(k):
        voteIlabel = labels[sortedIndicies[i]]
        class_count[voteIlabel] = class_count.get(voteIlabel, 0) + 1
    sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)  # 元组列表
    return sorted_class_count[0][0]

感想与总结

  1. 数据集dataSet的数据类型肯定是numpy array,因为需要做计算。标签向量的数据类型没有严格要求,可以是python list,也可以是numpy array;并且标签本身的数据类型也可以是整数或者字符。

  2. 排序使用argsort(),不直接用sorted和sort的原因:想保留原始数据的样子;对数据集进行排序的话,相应的标签lables也要同步操作,但是这两个数据是分开的两个对象,所以用index会比较方便。

  3. 注意: 字典本身不是可迭代对象,dict.itmes()/ dict.keys() / dict.values() 才是。

  4. 自己写的 和 书上的 主要区别在于,书上将 提取距离最小的k个点的标签 和 计算频率 一起在一个循环里实现了.

  5. 记住下面计算频率,并记录于字典的写法:

for i in range(k):
        voteIlabel = labels[sortedIndicies[i]]
        class_count[voteIlabel] = class_count.get(voteIlabel, 0) + 1
  1. 记住下面字典按值排序的写法:
sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True) 
  1. 其他
sorted_class_count = sorted(class_count.items(), key=operator.itemgetter(1), reverse=True)  # 元组列表

注意class_count.items()

class_count[voteIlabel] = class_count.get(voteIlabel, 0) + 1

这行代码集 初始化0+计数 于一体.
class_count.get(voteIlabel, 0) 字典的get()方法可以好好利用.

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值