k-均值聚类

k-均值聚类的一般过程:

  1. 收集数据:anyway
  2. 准备数据:需要数值型数据来计算距离
  3. 分析数据:anyway
  4. 训练算法:不适用于无监督学习,即无监督学习没有训练过程
  5. 测试算法:应用聚类算法、观察结果。可使用量化的误差标准如误差平方和
  6. 使用算法:簇质心可以代表整个簇的数据来做出决策

K均值聚类算法

#import pprint
from numpy import *
def loadDataSet(filename):
    dataMat=[]
    fr=open(filename)
    for line in fr.readlines():
        #默认删除空白符(包括'\n', '\r',  '\t',  ' ')
        curline=line.strip().split('\t')
        #python 3.x map return iterator not list as python2.x
        fltLine=list(map(float,curline))
        dataMat.append(fltLine)
    return dataMat


#计算向量的欧式距离
def distEclud(vecA,vecB):
    return sqrt(sum(power(vecA-vecB,2)))


#构建包含k个随机质心的集合,其数据点在边界之内(min~max)
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
# =============================================================================
# Examples
#     --------
# np.random.rand(3,2)
#     array([[ 0.14022471,  0.96360618],  #random
#            [ 0.37601032,  0.25528411],  #random
#            [ 0.49313049,  0.94909878]]) #random
# =============================================================================

def kMeans(dataSet,k,distMeas=distEclud,createCent=randCent):
    m=shape(dataSet)[0]#行数,即数据集中数据点的总数
    #簇分配结果矩阵:一列记录簇索引值,二列记录存储误差(当前点到簇质心的距离),可用来评价聚类效果
    clusterAssment=mat(zeros((m,2)))
    centroids=createCent(dataSet,k)#随机创建初始质心,k*n矩阵
    clusterChanged=True
    while clusterChanged:#结束条件:簇分配结果不再改变
        clusterChanged=False
        #遍历所有数据找到距离每个点最近的质心
        for i in range(m):
            #inf(正无穷) Nan(负无穷)
            minDist=inf;minIndex=-1;
            for j in range(k):
                #计算质心到数据点的欧式距离,此处依次计算每个质心到数据点i的距离
                distJI=distMeas(centroids[j,:],dataSet[i,])#dataSet[i,:]=dataSet[i,]
                
                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):
            #获得距离同一个质心最近的所有点的下标,即同一簇的坐标
            #nonzero 返回的是矩阵中所有非零元素的坐标,坐标的行数与列数个存在一个数组或矩阵当中  
            # 矩阵支持检查元素的操作,所有可以写成matrix == int这种形式,返回的一个布尔型矩阵,代表矩阵相应位置有无此元素
            #clusterAssment[:,0].A---Return self as an ndarray object.Equivalent to np.asarray(self).
            ptsInClust=dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]
            #axis=0 朝y轴负方向计算均值(即矩阵的列方向)
            centroids[cent,:]=mean(ptsInClust,axis=0)
    return centroids,clusterAssment#返回质心和点分配结果



使用后处理提高聚类性能---如何判断k是否正确;生成的簇是否比较好

        方法:合并最近的质心,或者合并两个使得SSE增幅最小的质心

        1.计算所有质心之间的距离,然后合并距离最近的两个点来实现

        2.合并两个簇然后计算SSE(误差平方和)总值

      


二分K-均值算法

伪代码:

将所有点看成一个簇

当簇数目小于k时

        对于每一个簇

               计算总误差

              在给定的簇上面进行k-均值聚类(k=2)

              计算将该簇一分为二后的总误差

       选择使得误差最小的那个簇进行划分工作          
    

#type(dataSet) is matrix
def biKmeans(dataSet,k,distMeas=distEclud):
    m=shape(dataSet)[0]
    clusterAssment=mat(zeros((m,2)))
    #(.tolist) transport matrix to list and return first cloumn
    #[[-0.10  0.05]]-->[[-0.10,0.05]]-->[-0.10,0.05]
    centroid0=mean(dataSet,axis=0).tolist()[0]
    centList=[centroid0]#[-0.10,0.05]->[[-0.10,0.05]] 簇
    #calc the dist between the first centroid0 ans per datapoint
    for j in range(m):
        clusterAssment[j,1]=distMeas(mat(centroid0),dataSet[j,:])**2
    #不停对簇进行划分,直到得到想要的簇数目
    while(len(centList)<k):
        lowestSSE=inf# sum of squared Error (error between sample and centroid)
        for i in range(len(centList)):# 依次遍历centList中的每个簇
            #将第i个簇中的所有点看成数据集,并输入带kMeans()中处理,k=2
            ptsInClurrCluster=dataSet[nonzero(clusterAssment[:,0].A==i)[0],:]
            centroidMat,splitClustAss=kMeans(ptsInClurrCluster,2,distMeas)#质心,簇
            
            sseSplit=sum(splitClustAss[:,1])#划分后数据集簇的误差值
            sseNoSplit=sum(clusterAssment[nonzero(clusterAssment[:,0].A!=i)[0],1])#剩余数据集(未参与划分)的误差
            print('sseSplit and notSplit',sseSplit,sseNoSplit)
            if(sseSplit+sseNoSplit<lowestSSE):
                bestCentToSplit=i
                bestNewCents=centroidMat
                bestClustAss=splitClustAss.copy()
                lowestSSE=sseSplit+sseNoSplit
                
        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,:].tolist()[0]#replace a centroid with two best centroids 
        centList.append(bestNewCents[1,:].tolist()[0])
        #reassign new clusters, and SSE
        clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:]= bestClustAss
    return mat(centList), clusterAssment



猜你喜欢

转载自blog.csdn.net/boahock/article/details/78629188