k均值聚类是一种无监督学习方法,当数据量小,数据维度低时,具有简单、快速、方便的优点,但是当数据量较大时,其速度较慢,也容易陷入局部最优。
和以前一样,kMeans聚类的原理在网上有很多讲解,所以这里不在赘述,直接给出步骤,而通过伪代码将是一个描述步骤的不错选择:
随机初始化k个聚类中心
while 有样本所属的聚类中心发生改变时:
for 每个样本i:
初始化最短距离dMin为无穷大
初始化距离其最近的簇中心的索引为jNearest
for 每个聚类中心j:
计算当前样本到当前聚类中心的距离di
如果di小于dMin:
dMin<-di
jNearest=j
对每个样本所属于的簇进行标记
计算各个簇中的样本的中心点,作为新的聚类中心
我们第一件要做的事就是定义一个能够在合理范围内随机产生聚类中心的函数,主要思想是,通过计算数据每个特征的最大值和最小值,然后在该范围内随机选择一个值即可:
import numpy as np
def randCenters(data:np.matrix,k:int):
n=data.shape[1]
cent=np.mat(np.zeros((k,n))) # 初始化k个质心
for j in range(n):
minJ=data[:,j].min()
maxJ=data[:,j].max()
rangeJ=float(maxJ-minJ) # 第j个特征的范围
cent[:,j]=minJ+rangeJ*np.random.rand(k,1)
return cent
我们还需要做一件事情,那就是完成距离的计算的函数,显然,这个函数将会被多次调用,用于计算当前样本与当前聚类中心的距离,你可以自定义距离的计算方式,比如我选择的是欧几里得距离:
def distance(a: np.matrix, b: np.matrix):
"""计算a,b两个向量之间的欧几里得距离"""
return np.sqrt(np.sum(np.power(a-b,2)))
我并不打算直接将一大串最终的实现代码给出,因为这样不便于读者看出每段代码的目的,所以我将先将代码分功能讲解,最后在给出完整的代码。
m=data.shape[0]
clusterCenter=randCenters(data,k)
clusterChanged=True
clusterAss-np.mat(np.zeros((m,2)))
其中,m是样本的个数,clusterChanged是一个标志,方便我们控制接下来的while循环。
最令人费解的可能是clusterAss,它的第一列将为每个样本做标记,标记其属于哪个簇,第二列将保存该样本到该簇的距离distance.
while clusterChanged:
clusterChanged=False
for i in range(m): # 对每个样本,找到其归属,并计算到该归属的距离
minDist=np.inf
belongClust=-1
for j in range(k): # 为当前样本遍历每个簇中心
dist=distance(data[i,:],cluterCenter[j,:])
if dist<minDist:
minDist=dist
belongClust=j
# 看看当前样本所属的簇有没有发生变化,如果有,则改变标志clusterChanged,继续迭代
if clusterAss[i,:].A[0][0] != j:
clusterChanged=True
clusterAss[i,:]=belongClust,minDist
上面的代码所做的事如下:计算每个样本对所有聚类中心的距离,然后按照距离最小的原则,给出其归属,并将该归属和对应的距离保存到clusterAss中。
不要忘了,我们以上所做的工作只是对我们最开始所随机产生的聚类中心所做的计算,我们还需要根据每次聚类的结果,更新每个簇所在的簇中心。这里我们使用每个簇内样本点的平均值来作为其几何中心,也就是更新后的新的簇中心:
for c in range(k):
pointsInCluster=data[np.nonzero(clusterAss.A[:,0]==c)[0],:] # 获取属于簇c的数据,数组切片一定要熟练!
clusterCenter[c,:]=np.mean(pointsInCluster,axis-0)
return clusterCenter,clusterAss
def kMeans1(data:np.matrix, k=3):
m=data.shape[0]
clusterAss=np.mat(np.zeros((m,2)))
clusterChanged=True
clusterCenter=randCent(data,k) # 随机初始化各个簇的质心
while clusterChanged:
clusterChanged=False
for i in range(m): # 对每个样本
minDist=np.inf
belongClust=-1
for j in range(len(clusterCenter)): # 对每个聚类中心
dist=distEclud(data[i,:].A[0],clusterCenter[j,:].A[0])
if dist < minDist:
minDist=dist
belongClust=j
if clusterAss[i,:].A[0][0]!=belongClust:
clusterChanged=True
clusterAss[i,:]=belongClust,minDist
for c in range(k):
pointsInClust = data[np.nonzero(clusterAss[:, 0].A == c)[0], :]
clusterCenter[c,:]=np.mean(pointsInClust,axis=0) # 更新聚类中心
return clusterCenter,clusterAss
纯手写不易.
——by 神采的二舅