K-means方法是一種非監督學習的算法,它解決的是聚類問題。
1、算法簡介:K-means方法是聚類中的經典算法,數據挖掘十大經典算法之一;算法接受參數k,然後將事先輸入的n個數據對象劃分爲k個聚類以便使得所獲得的聚類滿足聚類中的對象相似度較高,而不同聚類中的對象相似度較小。
2、算法思想:以空間中k個點爲中心進行聚類,對最靠近他們的對象歸類,通過迭代的方法,逐次更新各聚類中心的值,直到得到最好的聚類結果。
3、算法描述:
(1)適當選擇c個類的初始中心;
(2)在第k次迭代中,對任意一個樣本,求其到c各中心的距離,將該樣本歸到距離最短的那個中心所在的類;
(3)利用均值等方法更新該類的中心值;
(4)對於所有的C個聚類中心,如果利用(2)(3)的迭代法更新後,值保持不變,則迭代結束;否則繼續迭代。
4、算法舉例:
我們假設藥物A、B、C、D有兩個特徵值,分別是藥物重量以及PH值。
藥物名稱 | 藥物重量 | 藥物PH值 |
A | 1 | 1 |
B | 2 | 1 |
C | 4 | 3 |
D | 5 | 4 |
現在我們要對這四個藥物進行聚類,已知我們要分成兩類,那麼我們該怎麼做呢?
首先我們把上面的數據畫到二位座標系當中A(1,1),B(2,1),C(4,3),D(5,4):
初始時,我們先假設藥物A爲聚類1的中心點,B爲聚類2的中心點,那麼初始時的中心座標分別爲c1=(1,1),c2=(2,1),矩陣D的第一行代表各個點到中心點c1c1的距離,第二行代表各個點到中心點c2c2的距離;那麼初始矩陣D0D0表示成如下:
矩陣GG代表樣本應該歸屬於哪個聚類,第一行代表各個點是否屬於中心c1c1所在的類(0代表不在,1代表在),第二行代表各個點是否屬於中心c2c2所在的類(0代表不在,1代表在);那麼此時G0G0表示成如下:
由矩陣G0G0可知A藥物屬於一個類,B、C、D屬於一類;
然後,利用均值等方法更新該類的中心值。
c1=(1,1)
c2=((2+4+5)/3,(1+3+4)/3)=(13/3,8/3)
上圖是更新後的座標圖,對應的中心點也發生了變化。
因爲中心點跟上次不一樣了,所以我們又可以對樣本點進行重新劃分。劃分的方法還是跟以前一模一樣,我們先計算出矩陣D1表示成如下:
此時G1表示成如下:
由矩陣G1可知A、B藥物屬於一個類,C、D屬於一類;
然後,利用均值等方法再次更新該類的中心值。
c1=((1+2)/2,(1+1)/2)=(1.5,1)
c2=((4+5)/2,(3+4)/2)=(4.5,3.5)
上圖是更新後的座標圖,對應的中心點也發生了變化。
因爲中心點跟上次不一樣了,所以我們又可以對樣本點進行重新劃分。劃分的方法還是跟以前一模一樣,我們先計算出矩陣D2表示成如下:
此時G2表示成如下:
由矩陣G2可知A、B藥物屬於一個類,C、D屬於一類;
然後,利用均值等方法再次更新該類的中心值。
c1=((1+2)/2,(1+1)/2)=(1.5,1)
c2=((4+5)/2,(3+4)/2)=(4.5,3.5)
因爲對應的中心點並沒有發生變化,所以迭代停止,計算完畢。
本算法的時間複雜度:O(tkmn),其中,t爲迭代次數,k爲簇的數目,m爲記錄數,n爲維數;
空間複雜度:O((m+k)n),其中,k爲簇的數目,m爲記錄數,n爲維數。
適用範圍:
K-menas算法試圖找到使平凡誤差準則函數最小的簇。當潛在的簇形狀是凸面的,簇與簇之間區別較明顯,且簇大小相近時,其聚類結果較理想。前面提到,該算法時間複雜度爲O(tkmn),與樣本數量線性相關,所以,對於處理大數據集合,該算法非常高效,且伸縮性較好。但該算法除了要事先確定簇數K和對初始聚類中心敏感外,經常以局部最優結束,同時對“噪聲”和孤立點敏感,並且該方法不適於發現非凸面形狀的簇或大小差別很大的簇。
缺點:
1、聚類中心的個數K 需要事先給定,但在實際中這個 K 值的選定是非常難以估計的,很多時候,事先並不知道給定的數據集應該分成多少個類別才最合適;
2、Kmeans需要人爲地確定初始聚類中心,不同的初始聚類中心可能導致完全不同的聚類結果。(可以使用K-means++算法來解決)
Python代碼實現
# -*- coding:utf-8 -*- from numpy import * def loadDataSet(filename): dataMat = [] fr = open(filename) for line in fr.readlines(): curline = line.strip().split('\t') fltline = map(float,curline) dataMat.append(fltline) return dataMat #計算兩個向量的距離,歐式距離 def distE(vecA,vecB): return sqrt(sum(power(vecA-vecB,2))) #隨機選擇中心點 def randCent(dataSet,k): n = shape(dataSet)[1] centriods = mat(zeros((k,n))) for j in range(n): minJ = min(dataSet[:,j]) rangeJ = float(max(array(dataSet)[:,j])-minJ) centriods[:,j] = minJ+rangeJ*random.rand(k,j) return centriods def kmeans(dataSet,k,disMea=distE,createCent=randCent): m = shape(dataSet)[0] clusterA= mat(zeros((m,2))) centriods = createCent(dataSet,k) clusterC=True while clusterC: clusterC=False for i in range(m): minDist = inf minIndex = -1 for j in range(k): distJI = disMea(centriods[j,:],dataSet[i,:]) if distJI<minDist: minDist=distJI;minIndex=j if clusterA[i,0] != minIndex: clusterC = True clusterA[i,:] = minIndex,minDist**2 print(centriods) for cent in range(k): ptsInClust = dataSet[nonzero(clusterA[:, 0].A == cent)[0]] # get all the point in this cluster centriods[cent, :] = mean(ptsInClust, axis=0) # assign centroid to mean return centriods,clusterA def show(dataSet, k, centriods, clusterA): import matplotlib.pyplot as plt numSamples, dim = dataSet.shape() mark = ['or', 'ob', 'og', 'ok', '^r', '+r', 'sr', 'dr', '<r', 'pr'] for i in range(numSamples): markIndex = int(clusterA[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(centriods[i, 0], centriods[i, 1], mark[i], markersize=12) plt.show() def main(): dataMat = mat(loadDataSet('testSet.txt')) myCentroids, clustAssing = kmeans(dataMat, 4) print(myCentroids) show(dataMat, 4, myCentroids, clustAssing) if __name__ == '__main__': main()