1. 程式人生 > 實用技巧 >二分K-均值聚類演算法

二分K-均值聚類演算法

#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('\t')
        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”資料和聚類的動態視訊