KNN算法建立在K-means算法的基础上,即我们已经得到了聚类后的数据点簇后,输入一个新的数据点,将它归为现有的簇中。
KNN算法的步骤如下:
-
准备训练数据:收集包含特征和标签的训练数据集。
-
选择K值:确定K值,即在预测时考虑最近数据点的数量。K值一般是一个正整数,根据实际问题和数据集的特点进行选择。
-
计算距离:对于新样本,计算其与训练数据集中每个样本之间的距离。常用的距离度量方法有欧氏距离(两点间距离公式)、曼哈顿距离等。
-
选择最近邻:根据计算的距离,选择与新样本最近的K个邻居。
-
排序:对于分类问题,通过对这K个邻居的标签进行排序。
-
输出预测结果:根据排序结果,选取最高的标签,即为新样本的预测标签。
代码实现:
import numpy as np
import matplotlib.pyplot as plt
import math
def printpoint_add(tpPoints, size, color='b', marker=None):
plt.scatter(*zip(*[[point[0], point[1], size, color]
for point in tpPoints]),
marker=marker)
def printpoint_show():
plt.show()
def randpoint(size=0):
if size == 0:
return (np.random.randint(0, 10) + np.random.rand(),
np.random.randint(0, 10) + np.random.rand())
else:
point = list()
for _ in range(size):
point.append(randpoint())
return point
def distance(point, centroid):
return math.sqrt((point[0] - centroid[0])**2 + (point[1] - centroid[1])**2)
#遍历分类的距离,找到距离参数对应的数据类代号
def findClass(case_dist):
for i in range(countCluster):
if case_dist in dicDistance[i]:
dicDistance[i].remove(case_dist)
return i
return -1
countCluster = np.random.randint(2, 4) #簇总数
listCentroid = randpoint(size=countCluster) #质心点集
dicElement = dict() #簇代号-->数据点集,实现基于簇序号将数据点聚类
dicDistance = dict() #簇代号-->新数据点和簇中数据点的欧几里得距离集
listDistance = list() #全部欧几里得距离集
listPoint = randpoint(size=np.random.randint(100, 300)) #全部数据点集
listColor = ['r', 'b', 'g', 'c', 'm', 'y', 'k', 'w'] #plt颜色列表
count = 100
while count:
#每次循环都清空原有分类点,以便重新分类
for i in range(countCluster):
dicElement[i] = list()
#以质心为中心,使数据点聚集成簇
for point in listPoint:
dist = math.inf
index = 0
#遍历质心,找到距离最近的质心序号(index)
for i, centroid in enumerate(listCentroid):
newdist = distance(point, centroid)
if newdist < dist:
dist = newdist
index = i
#数据点入簇
dicElement[index].append(point)
#以簇为单位,更新质心位置
for i, points in dicElement.items():
if (length := len(points)) == 0: continue
x, y = 0, 0
for point in points:
x += point[0]
y += point[1]
x /= length
y /= length
listCentroid[i] = (x, y)
count -= 1
for i, centroid in enumerate(listCentroid):
printpoint_add([centroid], 120, listColor[i], '*')
printpoint_add(dicElement[i], 20, listColor[i])
newPoint = randpoint()
for i, points in dicElement.items():
dicDistance[i] = list()
for point in points:
dist = distance(newPoint, point)
dicDistance[i].append(dist)
listDistance.append(dist)
listDistance.sort()
K = 50
i = 0
listDistCount = [0 for _ in range(countCluster)] #对应簇序号的距离命中数
while K:
if i > len(listDistance) - 1: break
index = findClass(listDistance[i])
if index == -1:
i += 1
continue
else:
listDistCount[index] += 1
i += 1
K -= 1
maxClusterIndex = listDistCount.index(max(listDistCount))
print('各分类命中次数:', listDistCount)
print('最多命中簇序号:', maxClusterIndex)
printpoint_add([newPoint], 200, listColor[maxClusterIndex], '^')
printpoint_show()
运行效果(三角形即为新数据点,被正确归为绿色簇中):