0%

Numpy 实现 K_means 算法

代码所需数据
聚类就是根据数据之间的相似度将数据集划分为多个类别或组,使类别内的数据相似度较大而类别间的数据相似度较小。如下图所示,左边是原始数据,右边是聚类之后的效果,不同的颜色代表不同的类别。
在这里插入图片描述

对于本次代码聚类步骤如下(聚类算法大体步骤,可根据需求进行修改):

1.设置初始类别中心和类别数,初始化是要注意在题目所给数据的x、y的最小值和最大值进行。
2.根据类别中心对全部数据进行类别划分:每个点分到离自己距离最小的那个类
3.重新计算当前类别划分下每个类的中心:例如可以取每个类别里所有的点的平均值作为新的中心。如何求多个点的平均值? 分别计算X坐标的平均值,y坐标的平均值,从而得到新的点。注意:类的中心可以不是真实的点,虚拟的点也不影响。
4.在新的类别中心下继续进行类别划分;

如果连续两次的类别划分结果不变则停止算法; 否则循环2~5。例如当类的中心不再变化时,跳出循环。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import numpy as np
import matplotlib.pyplot as plt
%matplotlib

data = np.loadtxt('Lab4.dat')

def calSSE(X, cidx, ctrs) :
SSE = 0
for i, ctr in enumerate(ctrs) :
SSE += np.sum(np.square(X[np.where(cidx == i + 1)] - ctr))

return SSE / X.shape[0]

def kmeans(X, K) :
center_point = []
for i in range(K) :
point_x = np.random.uniform(np.min(X, axis = 0)[0], np.max(X, axis = 0)[0])#随机初始化簇心
point_y = np.random.uniform(np.min(X, axis = 0)[1], np.max(X, axis = 0)[1])
center_point.append([point_x, point_y])
center_point = np.array(center_point)
cluter = np.zeros(X.shape[0]).astype(np.int32)#建立簇类初始化为0
item = 5
while item > 0:#迭代
for i in range(X.shape[0]) :#计算每一组数据与每个簇心的欧氏距离,距离最小者记为此组数据为所标类别
distance = center_point
distance = np.sum(np.square(distance - X[i]), axis = 1)#注意x, y都计算所以要求和,注意求和维度
cluter[i] = np.argmin(distance) + 1#最小值的下标

New_center_point = np.zeros((K, 2))

for i in range(K) :#更新簇心,取每一簇类的平均值作为新簇心
New_center_point[i][0] = np.mean(X[np.where(cluter == i + 1), 0])
New_center_point[i][1] = np.mean(X[np.where(cluter == i + 1), 1])
if (New_center_point - center_point < 1e-7).all() :#当新簇心与之前的簇心近似相等时退出迭代
break
center_point = New_center_point
item -= 1
return cluter, center_point#返回每一组数据所对应的簇类和簇心

SSE = []
mark = [ 'r', 'c', 'y', 'k', 'm', 'g']

plt.ion()
for K in range(2, 7) :
cidx, ctrs = kmeans(data, K)
ctrs_set.append(ctrs)
print(f'K为{K}时的簇心 : \n {ctrs}')
SSE.append(calSSE(data, cidx, ctrs))#手肘法求最好分类的K值

plt.subplot(2, 3, K - 1)
for i in range(K) :
plt.scatter(data[np.where(cidx == i + 1), 0], data[np.where(cidx == i + 1), 1], marker = '.', color = mark[i])#作图
plt.scatter(ctrs[ : , 0], ctrs[ : , 1], marker = '*', color = 'g')#做出簇心
plt.title(f'K is {K}')
plt.tight_layout()
plt.xticks([]), plt.yticks([])


plt.figure()
plt.plot(list(range(2, 7)), SSE, '+--')
plt.ioff()
plt.show()

效果:
在这里插入图片描述

在这里插入图片描述