1 算法思想
聚类的基本思想是将数据集中的样本划分为若干个通常是不相交的子集,每个子集称为一个”簇”(cluster)。划分后,每个簇可能有相同对应的概念(性质)。K-均值算法就是一个使用很广泛的聚类算法,其中的K就表示簇的数量,K-means简单的说就是通过质心来将样本划分成K个不同的簇。
伪代码为:
工作流程:
(1)事先选定K个聚类中心
(2)计算待测样本a到每个聚类中心的距离,然后将样本a分配到距离最近的聚类中
(3)计算每个聚类样本的均值来动态更新原聚类质心
(4)不断迭代(2),(3),直到簇质心不再改变。
2 实现
def kMeans(dataSet, k, distMeas=distEclud, createCent=randCent):
m = shape(dataSet)[0] #m=80L
clusterAssment = mat(zeros((m,2)))
#创造一个m个列表,每个列表中2个元素的全零变量
#在matlab中相当于创建一个m行2列的矩阵
#第一列存簇索引值,第二列存当前点到簇质心的距离
centroids = createCent(dataSet, k) #随机创建k个簇心
clusterChanged = True #创建标注标量,用来达到条件就终止循环
while clusterChanged:
clusterChanged = False
for i in range(m):#遍历每个样本
minDist = inf #初始样本到簇质心的距离 nif 表示正无穷;-nif表示负无穷
minIndex = -1
for j in range(k): #遍历每个簇质心
distJI = distMeas(centroids[j,:],dataSet[i,:]) #计算样本点到簇质心的距离
if distJI < minDist: #寻找距离最近的簇质心
minDist = distJI
minIndex = j #将样本分配到距离最小的质心那簇
if clusterAssment[i,0] != minIndex: clusterChanged = True #如果样本分配结果发生变化,更改标志变量
#如果分配后结果不变,就将结果存入clusterAssment,结束while循环
clusterAssment[i,:] = minIndex,minDist**2
print centroids
for cent in range(k):#遍历每个簇质心
#找到每个簇质心对应的样本
ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]#get all the point in this cluster
centroids[cent,:] = mean(ptsInClust, axis=0) #计算这些样本的均值,作为该簇的新质心
return centroids, clusterAssment
3 二分K-均值算法
K-means虽然实现很容易,但却很容易收敛到局部最小,达不到全局最小,所以有人提出二分K-均值。二分K-均值简单来说就是将所有数据集分成两簇,然后再选择误差最小的那个簇再进行二分K-均值划分。
二分K-均值伪代码:
3.1 实现
def biKmeans(dataSet, k, distMeas=distEclud):
m = shape(dataSet)[0] #60L
clusterAssment = mat(zeros((m,2)))
centroid0 = mean(dataSet, axis=0).tolist()[0] #计算每维的均值 如果axis=1,就是计算每个列表元素的均值
centList =[centroid0] #用每维均值创建初始簇质心,也就是说,将整个数据集当成一簇
for j in range(m):#遍历每个样本
#保存样本到每个簇质心的距离
#所以此处shape(clusterAssment)=(60L,2L)
clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2
while (len(centList) < k): #如果初始的簇个数小于k
lowestSSE = inf
for i in range(len(centList)): #遍历初始的那簇
#提取当前簇中的所有样本点
ptsInCurrCluster = dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
#将这些样本点进行kmeans操作,其中K设为2.
# 也就是说,把之前那簇数据集分成两簇,分别计为:0簇和1簇,同时返回新分成的两簇中的每簇的质心和误差
#shape(centroidMat)=(2L,2L) shape(splitClustAss)=(60L,2L)
centroidMat, splitClustAss = kMeans(ptsInCurrCluster, 2, distMeas)
sseSplit = sum(splitClustAss[:,1])# 所有误差相加
sseNotSplit = sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])
print "sseSplit, and notSplit: ",sseSplit,sseNotSplit
#选择误差小的簇继续二分K-均值划分
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
#将簇编号(0,1)修改成划分簇及新加簇的编号
bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] = len(centList) #change 1 to 3,4, or whatever
bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] = bestCentToSplit
print 'the bestCentToSplit is: ',bestCentToSplit
print 'the len of bestClustAss is: ', len(bestClustAss)
#新划分的质点更新到centList中
centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]#replace a centroid with two best centroids
centList.append(bestNewCents[1,:].tolist()[0])
#新划分的结果更新到clusterAssment中
clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss#reassign new clusters, and SSE
return mat(centList), clusterAssment