聚類分析原理(及Python實現K-means代碼詳解)

聚類分析概念

聚類分析(cluster analysis ):是一組將研究對象分爲相對同質的羣組(clusters)的統計分析技術。聚類分析也叫分類分析,或者數值分類。聚類的輸入是一組未被標記的樣本,聚類根據數據自身的距離或者相似度將其劃分成若干個組,劃分的原則是組內距離最小化而組間(外部)距離最大化。聚類和分類的不同在於:聚類所要求劃分的類是未知的。

距離量度

不同的距離量度會對距離的結果產生影響,常見的距離量度如下所示:
在這裏插入圖片描述
在這裏插入圖片描述

聚類研究分析的方法

1.層次的方法(hierarchical  method)

2.劃分方法(partitioning method)

3.基於密度的方法(density-based method)DBSCAN

4.基於網格的方法(grid-based method)

5.基於模型的方法(model-based method)

在這裏插入圖片描述

K-means算法

K-means算法是經典的聚類分析劃分方法之一。其優缺點如下:

優點:
原理簡單
速度快
對大數據集有比較好的伸縮性

缺點:
需要指定聚類 數量K
對異常值敏感
對初始值敏感

受離羣點的影響較大,由於其迭代每次的中心點到全部樣本點的距離和的平均值。
以歐式距離來衡量距離大小,使用誤差平方和(Sum of the Squared Error,SSE)作爲聚類的目標函數:
在這裏插入圖片描述
k表示k個聚類中心,ci表示第幾個中心,dist表示的是歐幾里得距離。該公式表示求每個點到自己所屬聚類中心的距離之和。即先把每個點到自己聚類中心的距離求出來,然後將所有距離相加。要讓SSE最小,需每個點到自己聚類中心的距離最短。
在這裏插入圖片描述
由上可以看出,簇的最佳質心是簇中各點的均值。

K-means算法思想如下:

任選k個初始點作爲質心
repeat  :
		數據集中每一個數據點
				每一個初始質心
						計算數據點與質心的距離
				將數據點分配到距離最近的簇
		對每個簇計算簇的均值,將均值作爲簇的新的質心
until:
		任一數據點的簇分配結果沒有發生改變,或者達到最大迭代次數

具體實現代碼如下:
(光看代碼沒用,可將下面代碼直接copy到IDE中跑下,看看效果。也可通過IDE單步運行功能看看程序的具體的走向)

#-*- coding: utf-8 -*-
#python實現聚類
#import numpy as np
from numpy import *

#加載數據集
def loadDataSet(fileName):
    dataMat = []
    fr = open(fileName)
    for line in fr.readlines():
        """
        strip()將line行開頭和結尾的空白符號刪掉。split('\t') 函數以‘\t’爲切分符號對每行切分
        split函數返回的結果是一個列表,賦值給curLine 
        """
        curLine = line.strip().split('\t')
        """#map()是 Python 內置的高階函數,它接收一個函數 f 和一個 list,
        並通過把函數 f 依次作用在 list 的每個元素上,得到一個新的 list 並返回。"""
        fltLine = map(float, curLine)
        dataMat.append(fltLine)
    return dataMat

#計算歐式距離的函數
def countDist(vecA,vecB):
    return sqrt(sum(power(vecA-vecB,2)))

#測試countDist
# vecA,vecB = np.array([1,2]),np.array([3,4])
# x = countDist(vecA,vecB)
# print(x)

#隨機設置k個質心
def randCent(dataSet,k):
    n = shape(dataSet)[1]
    #初始化一個k行n列的二維數組,數組初始值全部爲0,然後用mat函數將其轉化爲矩陣
    centroids = mat(zeros([k,n]))
    for j in range(n):
        minj = min(dataSet[:,j])
        rangej = float(max(dataSet[:,j])-minj)
        centroids[:, j] = mat(minj + rangej * random.rand(k, 1))
    return centroids

#k-Means算法核心部分
def kMeans(dataSet, k, distMeas=countDist, createCent=randCent):
    #將函數distEclud和randCent作爲參數傳進來,可以更好的封裝kMeans函數
    m = shape(dataSet)[0] 
    """ clusterAssment是一個m行2列的矩陣,第一列存放每個樣本所屬聚類中心的下標,
        第二列存放該樣本與該聚類中心的距離
    """
    clusterAssment = mat(zeros((m,2)))
    centroids = createCent(dataSet, k)
    clusterChanged = True
    while clusterChanged:
        clusterChanged = False
        #下面兩個for循環計算每一個樣本到每一個聚類中心的距離
        for i in range(m):   #遍歷樣本
            minDist = inf     #inf表示無窮大,-inf表示負無窮
            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[i,:] = minIndex,minDist**2
            print(centroids)
        for cent in range(k):     #重新計算聚類中心,cent從0遍歷到k
            ptsInClust = dataSet[nonzero(clusterAssment[:,0].A==cent)[0]]  #nonzero函數:返回不爲0元素的行列下標組成的元組
            """  #clusterAssment[:,0]:取得clusterAssment中第0列元素。
                 #clusterAssment[:,0].A:轉置成一個1行m列的行矩陣
                 #clusterAssment[:,0].A==cent :將行矩陣中每一個元素與
                 cent進行比較,得到一個1行m列、元素取值爲True和false
                 的矩陣,元素爲true表示該樣本是第cent個聚類中心的點
                 #nonzero(clusterAssment[:,0].A==cent)[0]:獲得元素值爲
                 True的元素下標,這些下標集合即爲所有屬於cent類的樣 本下標
                 #整句含義:取得數據集中屬於第cent個簇的樣本集
            """
            centroids[cent,:] = mean(ptsInClust, axis=0)
    return centroids, clusterAssment
    
#顯示結果
def show(dataSet, k, centroids, clusterAssment):
    from matplotlib import pyplot as plt
    numSamples, dim = dataSet.shape  #dataSet.shape返回兩個值,分別賦給numSamples和dim
    mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr']  #樣本集的顯#示樣式
    for i in range(numSamples):
        markIndex = int(clusterAssment[i, 0])
        plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex])
    mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '<b', 'pb']  #聚類中#心的顯示樣式
    for i in range(k):
        plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 12)
    plt.show()

def main():
    #dataMat = mat(loadDataSet('testSet.txt'))  #加載數據集。若沒有源數據,可用下面兩句隨機生成數據
    dataMat = random.rand(100,2) #隨機生成100行2列的數據
    dataMat = mat(dataMat)
    myCentroids, clustAssing = kMeans(dataMat,4)
    print(myCentroids)
    show(dataMat, 4, myCentroids, clustAssing)

if __name__ == '__main__':
    """ python代碼按照順序從頭到尾執行,不執行def開頭的函數,def函數只有被調用時才執行。
    本句意思是:判斷正在執行的程序是否有主函數,若有,則不執行下面這行語句;若沒有,則調用main函數"""
    main()

參考:
k-means聚類算法python實現
數據挖掘-聚類分析(Python實現K-Means算法)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章