K最近邻算法(KNN)实例代码实现

K最近邻 (k-Nearest Neighbors,KNN) 算法是一种分类算法,也是最简单易懂的机器学习算法,没有之一。1968年由Cover 和 Hart提出,应用场景有字符识别、文本分类、图像识别等领域。该算法的思想是:一个样本与数据集中的k个样本最相似,如果这k个样本中的大多数属于某一个类别,则该样本也属于这个类别。还是直接讲例子最好懂,一直没找到好的例子,就改造了下Peter Harrington的《机器学习实战》中电影分类的例子,当然实际情况不可能这么简单,这里只是为了说明该算法的用法。

""" 思想:一个样本与数据集中的k个样本最相似(距离最近),如果这k个样本中的大多数属于某一个类别,
    则该样本也属于这个类别。"""

import math

# 一、构建一个已分好类的数据集。
movie_data = {"宝贝当家": [45, 2, 9, "喜剧片"],
              "美人鱼": [21, 17, 5, "喜剧片"],
              "澳门风云3": [54, 9, 11, "喜剧片"],
              "功夫熊猫3": [39, 0, 31, "喜剧片"],
              "谍影重重": [5, 2, 57, "动作片"],
              "叶问3": [3, 2, 65, "动作片"],
              "伦敦陷落": [2, 3, 55, "动作片"],
              "我的特工爷爷": [6, 4, 21, "动作片"],
              "奔爱": [7, 46, 4, "爱情片"],
              "夜孔雀": [9, 39, 8, "爱情片"],
              "代理情人": [9, 38, 2, "爱情片"],
              "新步步惊心": [8, 34, 17, "爱情片"]}

# 二、引入一个新样本:"唐人街探案": [23, 3, 17, "?片"]
x = [23, 3, 17]
KNN = []
for key, v in movie_data.items():
    """ 求每一个电影与新样本之间的 欧氏距离。"""
    d = math.sqrt((x[0] - v[0]) ** 2 + (x[1] - v[1]) ** 2 + (x[2] - v[2]) ** 2)
    KNN.append([key, round(d, 2)])
print(KNN)  # 此时KNN是一个列表,其中每个元素都是一个列表,内容都是这样的:['我的特工爷爷', 17.49]

# 三、按照距离大小进行递增排序。
KNN.sort(key=lambda dis: dis[1])  # dis: dis[1] 意思是 对KNN中每个列表的第二个位置的元素进行排序
print(KNN)

# 四、选取距离最小的k个样本。这里取 k=5
KNN = KNN[:5]
print(KNN)

# 五、确定前k个样本所在类别出现的频率,并输出出现频率最高的类别。
labels = {"喜剧片": 0, "动作片": 0, "爱情片": 0}
# print(labels)
for s in KNN:
    """ for s in KNN 循环,就是遍历KNN中的所有元素(都是列表)
        s[0]是指列表 s 中第一个位置的内容(电影名);而这些电影名在字典movie_data中是 键。
        所以 movie_data[s[0]] 代表 s[0] 对应的 键(电影名) 在字典中对应的 值。
        综上,label是一个列表,来自于字典movie_data的 值,易知,label[3]是指该列表中第四个位置的元素,也就是电影类型。
        而label[3]对应的电影类型在字典labels中是 键。那么labels[label[3]]就是某个键(电影类型)所对应的值(该类型的数量)。
        所以 labels[label[3]] += 1 意味着:某个电影类型出现一次,就将其数量累加一次。"""
    label = movie_data[s[0]]
    # print(s[0])
    # print(label)
    labels[label[3]] += 1
    # print(label[3])
labels = sorted(labels.items(), key=lambda l: l[1], reverse=True)
""" labels.items():遍历字典labels中的所有键值对,将每一个键值对定义成一个 元组;
    key=lambda:固定写法;l: l[1] 意思是对元组中 第二个元素 执行排序(这里是 电影类型的数量)
    注意:这波操作以后,labels已经不是字典了,变成了一个列表,原来的那些键值对,都变成了元组 """
print(labels, labels[0][0], sep='\n')  # labels[0][0]:labels中 第一个元组 的 第一个元素