zoukankan      html  css  js  c++  java
  • 二分K-均值聚类算法

     二分k均值聚类是k均值聚类的增强版:为克服K-均值算法收敛于局部最小值的问题,有人提出了另一个称为二分K-均值(bisecting K-means)的算法。

    #K-means聚类
    from numpy import *
    import matplotlib.pyplot as plt
    
    
    plt.ion()   #开启交互模式,实时绘制
    plt.subplots()
    plt.xlim(-6, 6)
    plt.ylim(-6, 6)
    #plt.pause(5)
    def loadDataSet(fileName):
        dataMat = []
        fr = open(fileName)
        for line in fr.readlines():
            curLine = line.strip().split('	')
            fltLine = list(map(float,curLine))
            dataMat.append(fltLine)
        return dataMat
    
    def distEclud(vecA, vecB):
        #print("vecA:",vecA,"vecB:",vecB)
        return sqrt(sum(power(vecA-vecB, 2)))   #欧式距离
    
    #给定数据集构建一个包含k个随机质心的集合
    #该函数为给定数据集构建一个包含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
    
    '''
    k-均值聚类算法:
    该算法会创建k个质心,然后将每个点分配到最近的质心。这个过程重复数次,直到数据点的簇分配结果不再改变为止。
    这个接口的缺点是:质心是随机初始化的,虽然最后会通过划分后的点加均值函数重新计算,但质心没有真正的进行最优化收敛
    k均值算法收敛(kMeans函数)到了局部最小值,而非全局最小值
    
    一种用于度量聚类效果的指标是SSE(Sum of Squared Error,误差平方和),SSE值越小表示数据点越接近于它们的质心,聚类
    效果也就越好。因为对误差取了平方,因此更加重视那些远离中心的点,一种肯定可以降低SSE值的方法是怎加簇的个数,
    但这违背了聚类的目标。聚类的目标是在保持簇数目不变的情况下提高簇的质量。
    '''
    def kMeans(dataSet, k, distMeans=distEclud, createCent=randCent):
        m = shape(dataSet)[0]
        clusterAssment = mat(zeros((m, 2)))     #记录每个样本点到质心的最小距离,及最小距离质心的索引
        centroids = createCent(dataSet, k)
        centroids_mean_before=centroids
        plt_scatter(dataSet,centroids,clusterAssment)
        clusterChanged = True
        while clusterChanged:
            clusterChanged = False
            for i in range(m):      #样本数据按行遍历
                minDist = inf; minIndex = -1     #inf 表示正无穷
                for j in range(k):      #遍历质心,寻找最近质心
                    distJI = distMeans(centroids[j,:], dataSet[i,:])    #计算i点到j质心的距离
                    if distJI < minDist:
                        minDist = distJI; minIndex=j    #计算出每个样本数据最近的质心点与距离
                if clusterAssment[i,0] != minIndex:    clusterChanged = True
                clusterAssment[i,:] = minIndex,minDist**2
            plt_scatter(dataSet,centroids_mean_before,clusterAssment)
            for cent in range(k):   #遍历所有的质心并更新它们的取值
                ptsInClust = dataSet[nonzero(clusterAssment[:,0].A == cent)[0]]
                centroids[cent,:] = mean(ptsInClust, axis=0)
                centroids_mean_before = centroids
            plt_scatter(dataSet,centroids,clusterAssment)
        return centroids, clusterAssment
    
    
    #二分K-均值算法
    '''
    为了克服k-均值算法收敛于局部最小值的问题,有人提出了另一个称为二分K-均值的算法。
    二分k-均值:首先将所有点作为一个簇,然后将该簇一分为二。之后选择其中一个簇继续进行划分,
    选择哪个簇继续划分取决于对其划分是否可以最大程度降低SSE的值。上述基于SSE的划分过程不断重复,
    直到得到用户指定的簇数目为止。
    '''
    def biKmeans(dataSet, k, distMeas=distEclud):
        m = shape(dataSet)[0]
        clusterAssment = mat(zeros((m,2)))  #创建一个m*2的零矩阵
        #print('clusterAssment:',clusterAssment)
        centroid0 = mean(dataSet, axis=0).tolist()[0]       #tolist() 把mat转为list
        centList = [centroid0]
        plt_scatter(dataSet, mat(centList), clusterAssment)
        #print('centList:', centList)
        for j in range(m):
            clusterAssment[j,1] = distMeas(mat(centroid0), dataSet[j,:])**2    #记录每个点到质心的距离的平方
        #print('clusterAssment:',clusterAssment)
        while (len(centList) < k):
            lowestSSE = inf     #正无穷
            for i in range(len(centList)):
                #以clusterAssment中第一列索引为i的行为索引,找到这些索引对应dataSet的的值返回。通俗讲 就是找出第i个质心关联的点
                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])  #求划分前其余(除i质心之外的)质心的sse值
                print("sseSplit, and notSplit:",sseSplit, sseNotSplit)
                if (sseSplit + sseNotSplit) < lowestSSE:
                    bestCentToSplit = i
                    bestNewCents = centroidMat
                    bestClustAss = splitClustAss.copy()
                    lowestSSE = sseSplit + sseNotSplit      #所有质心(被分割的质心加没被分割的质心)距离的平方
            #更新bestClustAss簇的分配结果
            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))
            #将bestCentTosplit(最适合分割的质心点),替换为分割后的第0个质心点,分割后生成的另一个质心点通过 append方法添加
            centList[bestCentToSplit] = bestNewCents[0,:].tolist()[0]   #python3修改
            centList.append(bestNewCents[1,:].tolist()[0])
            #将被分割的质心cluter索引和最小距离平方(minDist**2) 重新赋值
            clusterAssment[nonzero(clusterAssment[:,0].A == bestCentToSplit)[0],:] = bestClustAss
            plt_scatter(dataSet, mat(centList), clusterAssment)
        return mat(centList),clusterAssment    
    
    #绘图接口
    def plt_scatter(datMat,datCent,cluster):
        plt.clf()     # 清空画布
        plt.xlim(-6, 6)      # 因为清空了画布,所以要重新设置坐标轴的范围
        plt.ylim(-6, 6)
        m = shape(datMat)[0]
        n = shape(datCent)[0]
        ms=['^','v','o','s','p','<','>','1','2','3']    #数据点形状
        cs=['r','g','b','y','m','c','k','r','g','b']    #数据点的颜色
        
        for i in range(m):
            k=int(cluster[i,0])
            plt.scatter(datMat[i,0],datMat[i,1],c=cs[k],marker=ms[k],alpha=0.5)
        for j in range(n):
            plt.scatter(datCent[j,0],datCent[j,1],c=cs[j],marker='x',alpha=1.0)
        plt.pause(0.3)
        
    
    
    if __name__ == '__main__':
        datMat = mat(loadDataSet('testSet.txt'))
        #myCentroids,clusterAssing = kMeans(datMat, 4)
        #plt_scatter(datMat,myCentroids,clusterAssing)
        
        centList,myNewAssments=biKmeans(datMat,4)
        plt_scatter(datMat,centList,myNewAssments)
    
        plt.ioff()
        plt.show()

    链接:https://pan.baidu.com/s/1E-x93BbFhZ32CnucPpHOWw
    提取码:abt2

    网盘中数据为“testSet.txt”数据和聚类的动态视频

  • 相关阅读:
    让元素水平和垂直居中的方法总结
    a标签常见问题
    centos7的安装
    java面向对象-类的定义
    java内存划分
    java基础知识点-数组
    java基础知识点-重载
    第一章:服务器的组成知识点
    java 第二天运算符及录入函数Scanner
    java第一天 数据类型、变量的命名、类型的转换
  • 原文地址:https://www.cnblogs.com/go-ahead-wsg/p/13608805.html
Copyright © 2011-2022 走看看