一,K均值聚类算法
1,特点
- 优点:容易实现。
- 缺点:可能收敛到局部最小值,在大规模数据集上收敛较慢。
- 适用数据类型:数值型数据。
2,伪代码
创建k个点作为起点质心(经常是随机选择)
当任意一个点的簇分配结果发生改变时
对数据集中的每个数据点
对每个质心
计算质心与数据点之间的距离
将数据点分配到距其最近的簇
对每个簇,重新计算簇中每个点的均值并将均值作为质心
3,Python实现
from numpy import *
def loadDataSet(filename):
dataMat = []
fr = open(filename)
for line in fr.readlines():
lineArr = line.strip().split('\t')
fltLine = [float(t) for t in lineArr]
dataMat.append(fltLine)
return dataMat
def distEclud(vecA,vecB):
return sqrt(sum(power(vecA-vecB,2)))
def randCent(dataSet,k):
n = shape(dataSet)[1]
centroids = mat(zeros((k,n)))
for j in range(n):
minJ = min(dataSet[:,j])
rangeJ = float(max(dataSet[:,j])-minJ)
centroids[:,j] = minJ+rangeJ*random.rand(k,1)
return centroids
def kMeans(dataMat,k,distMeans=distEclud,createCent=randCent):
m = shape(dataMat)[0]
centroids = createCent(dataMat,k)
clusterAssment = mat(zeros((m,2)))
clusterChanged = True
while clusterChanged:
clusterChanged = False
for i in range(m):
minDist = inf;minIndex = -1
for j in range(k):
distJI = distMeans(dataMat[i],centroids[j])
if distJI < minDist:
minDist= distJI
minIndex = j
if clusterAssment[i,0]!=minIndex:
clusterChanged=True
clusterAssment[i] = minIndex,minDist**2
print(centroids)
for cent in range(k):
ptsInClust = dataMat[nonzero(clusterAssment[:,0]==cent)[0]] #找到同一个簇中的所有点
centroids[cent,:] = mean(ptsInClust,axis=0)
return centroids,clusterAssment
二,使用后处理来提高聚类性能
K均值算法收敛但聚类效果差的原因是,由于质心的随机初始化,K均值算法可能会收敛到局部最小值。
一种度量聚类效果的指标是SSE(误差平方和),但必须在保持簇数目不变的情况下提高簇的质量。
后处理:
- 将具有较大SSE值的簇划分成两个簇;
- 为了保持簇总数不变,然后再将某两个簇进行合并:合并最近的两个质心或者合并两个使得SSE增幅最小的质心。
三,二分K均值算法
1,原理
该聚类算法可以克服收敛于局部最小值的问题。
该算法首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,选择哪个簇取决于是否可以最大程度地降低SSE的值。上述基于SSE的划分不断重复,直到得到用户指定的簇数目为止。
当然也可以选择SSE最大的簇进行划分。
2,伪代码
将所有点看成一个簇
当簇数目小于k时
对于每一个簇
计算总误差
在给定的簇上面进行K均值聚类(k=2)
计算将该簇一分为二后的总误差
选择使得误差最小的那个簇进行划分操作
3,Python实现
def biKmeans(dataSet, k, distMeas=distEclud):
m = shape(dataSet)[0]
clusterAssment = mat(zeros((m,2)))
centroid0 = mean(dataSet, axis=0).tolist()[0]
centList =[centroid0]
for j in range(m):
clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2
while (len(centList) < k):
lowestSSE = inf
for i in range(len(centList)):
ptsInCurrCluster =\
dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
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)
if (sseSplit + sseNotSplit) < lowestSSE:
bestCentToSplit = i
bestNewCents = centroidMat
bestClustAss = splitClustAss.copy()
lowestSSE = sseSplit + sseNotSplit
bestClustAss[nonzero(bestClustAss[:,0].A == 1)[0],0] =\
len(centList)
bestClustAss[nonzero(bestClustAss[:,0].A == 0)[0],0] =\
bestCentToSplit
print('the bestCentToSplit is: ',bestCentToSplit)
print('the len of bestClustAss is: ', len(bestClustAss))
centList[bestCentToSplit] = bestNewCents[0,:]
centList.append(bestNewCents[1,:])
clusterAssment[nonzero(clusterAssment[:,0].A == \
bestCentToSplit)[0],:]= bestClustAss
return mat(centList), clusterAssment