K-Means K均值算法python实现 包括绘图
算法梗概
K-Means算法(K均值)是一种基于姓心的无监督聚类算法,用于把样本集中的对象分配到n个簇中,该算法的基本步骤:
1:随机产生n个中心点(n需要事先给定,n是多少,最后产生的簇就是多少)
2:计算每个样本点与中心点的距离(该距离不一定是我们通常认为的欧式距离,但在本例中,我将用欧式距离来计算)
3:把每一个点分配给距离最近的中心点
4:重新计算每个簇的中心点,并重复第2步
算法实现
直接上代码
import random as rd
import numpy as np
import matplotlib.pyplot as plt
X = np.array([[0, 0],
[1, 1],
[2, 1],
[4, 3],
[5, 3],
[5, 4],
[6, 5],
[1, 4],
[1, 5],
[1, 6]])
def K_Means(X, n):
# 随机生成n个中心点(用数组储存)
C_new = [] # 新的中心点
C_old = [] # 旧的中心点
style_color = ['r', 'y', 'b', 'c', 'm'] # 颜色类型
style_symbol = ['o', '^', 'h', 'd', 'D'] # 点的图形
current_color = [] # 每个点应该的颜色类型
pause_time = 0.5 # 画完一个点之后的暂停时间
for i in range(n):
C_new.append(list(rd.choice(X)))
print("随机生产的中心点有:", C_new)
# while(新的中心点不全等于旧的中心点):
while C_new != C_old:
plt.xlim(-1, 9) # 初始化画布坐标轴范围
plt.ylim(-1, 9)
# 分别计算每个点到n个中心点的距离
index = [] # 对应中心点在C_new中的下标
'''========把X中的点(和中心点)依次画出来(只适用与2维数组)=========='''
if not current_color:
x = np.array(X).transpose().tolist()
for i in range(X.shape[0]):
plt.plot(x[0][i], x[1][i], 'go')
plt.pause(pause_time)
else:
x = np.array(X).transpose().tolist()
for i in range(X.shape[0]):
plt.plot(x[0][i], x[1][i], 'o'+style_color[current_color[i]])
plt.pause(pause_time)
# 绘制中心点
y = np.array(C_new).transpose().tolist()
for i in range(len(C_new)):
plt.plot(y[0][i], y[1][i], 'D'+style_color[i])
plt.pause(pause_time)
'''==============================================='''
for i in range(X.shape[0]):
dis = [dist(X[i], C_new[j]) for j in range(len(C_new))] # 因为C_new是列表类型,他没有shape方法,只能用len
# 把最小距离的中心点的下标存入数组(一样大的话随便存)
index.append(dis.index(min(dis)))
print(index)
'''==============把属于同一个中心点的点染色============='''
current_color = index.copy() # 把索引复制给c_c
x = np.array(X).transpose().tolist()
for i in range(X.shape[0]):
plt.plot(x[0][i], x[1][i], 'o' + style_color[current_color[i]])
plt.pause(pause_time)
del x
'''==============================================='''
# 把新中心点赋值(copy)给旧中心点
C_old = C_new.copy()
C_new = []
# 分别取出相同下标的点集合,重新计算中心点并赋值给新的中心点
index = np.array(index)
for i in range(index.max() + 1):
points = X[np.where(index == i)]
C_new.append(list(center(points)))
print("新的中心点:", C_new)
'''===============判断是否应该结束绘图===================='''
if C_new != C_old:
plt.clf()
'''================================================'''
print('分类完毕')
plt.show()
# 计算距离
def dist(X1, X2): # X1 X2 必须都是数组类型
X1 = np.array(X1)
X2 = np.array(X2)
s = sum((X1 - X2) ** 2) ** 0.5
print(X1, "和", X2, "之间的距离为:", round(s, 2))
return s
# 计算中心点
def center(X):
# 次优解决方案!
Y = X[0]
for i in range(1, X.shape[0]):
Y = Y + X[i]
return Y / len(X)
K_Means(X, 3)
这段代码可以直接复制到自己的电脑里面跑一下,可能会出现一开始随机产生的中心点有两个相同的bug,我懒得debug了哈哈哈
被===== ====== 括起来的代码都是为了绘制出图写的,大家可以直接删掉,不影响代码的运行(如果要删的话不要忘记删掉前面的plt.xlim(),plt.ylim()和最后的plt.show()。
因为第一次使用matplotlib,对他的一些方法不太了解,所以写出来的代码非常冗长,如果不需要绘图的话,只需要大概40多行就可以完成任务。
代码分析:
计算两个点之间距离的代码:
def dist(X1, X2): # X1 X2 必须是数组类型
X1 = np.array(X1) # 为了初始化点的类型
X2 = np.array(X2)
s = sum((X1 - X2) ** 2) ** 0.5
print(X1, "和", X2, "之间的距离为:", round(s, 2))
return s
如两个点:x1:[1,2],x2:[3,4]
先计算两个点各个分量之间的差
x1 - x2 = [-2,-2]
再计算两个差的平方和再开发
计算中心点
def center(X):
Y = X[0]
for i in range(1, X.shape[0]):
Y = Y + X[i]
return Y / len(X)
n个点的中心点,如点:[2,3][4,4][5,6]
它的中心点是:([2,3]+[4,4]+[6,8])/3 = [4,5]
其中上面的分母3是点的个数,如果有4个点那就应该除以4了。我认为K-均值算法的均值就体现在这里。
到这里两个最基本的方法已经写好了,接下来过一下这个算法
K-Means(X,n)
其中的X是样本点集,n是你需要的簇的量,值的注意的是,X是一个二维数组,他每一行代表一个点。
我认为这段代码唯一需要说明一下的就是 :
index = [] # 对应中心点在C_new中的下标
这一小段莫名其妙的代码了。其他的地方我都用注释的方式写出了用处。
这段代码是啥意思呢?或者说这个index[]有啥用呢?他其实就是每一个样本点的类标号集合。比如我的样本中心点C_new里面有x,y,z这三个中心点,那index[]里面第0个元素的值(假设它为1),那他的意思就是X里面的第0个点离样本中心点C_new内下标为1的中心点距离最近
这个非常有用,如果要绘图的话,这段代码也可以派上用场
最后
这是我写的第一个博客,我也是一个刚入门的程序猿,如果有什么地方不对的,或者有更好的实现方法,欢迎留言!😁
ps:关于绘图还没有讲,不过原理十分简单,思想就是给每一个点都做一个标记,在遍历每个点的时候给每个点按照标记“覆盖”一层颜色(本质上是在原来的点的基础上在画一个新的点)。
效果图:
菱形是中心点位置,在自己电脑上运行的话,它会以动画的方式呈现