k-means k均值聚類 及二分k均值聚類

from numpy import *
def load_data(file_name):
    data=[]
    fr=open(file_name)
    for line in fr.readlines():
        cur_line=line.strip().split('\t')
        flt_line=map(float,cur_line)
        data.append(flt_line)
    return data
def distance(a,b):
    '''計算倆向量的歐式距離'''
    return sqrt(sum(power(a-b,2)))
def rand_cent(data,k):
    n=shape(data)[1]
    #n數據的列數
    centroids=mat(zeros((k,n)))
    #初始質點矩陣,k爲質點數
    for j in range(n):
        min_j=min(data[:,j])
        #選出第j列中最小的數
        range_j=float(max(data[:,j])-min_j)
        #用j列中最大的減去最小的
        centroids[:,j]=min_j+range_j*random.rand(k,1)
        #獲得隨機質點,質點在數據範圍之內
    return centroids
def k_means(data,k,distance=distance,create_cent=rand_cent):
    '''k均值聚類算法'''
    m=shape(data)[0]
    cluster_assment=mat(zeros((m,2)))
    #cluster_assment第一列爲質點,第二列爲距離
    centroids=create_cent(data,k)
    cluster_changed=True
    while cluster_changed:
        cluster_changed=False
        
        for i in range(m):
            min_distance=inf;min_index=-1
            #初始最小距離爲正無窮,最小索引爲-1
            for j in range(k):
                #j 相當於質點腳標
                #k爲4 ,則就有4個質點,j就是每個質點的腳標
                dist_ji=distance(centroids[j,:],data[i,:])
                #計算每個隨機質點與各數據的距離
                if dist_ji<min_distance:
                    #若隨機質點與數據的距離 ji 小於最小距離,則最小距離就繼承ji
                    #最小索引爲j
                    min_distance=dist_ji;min_index=j
            if cluster_assment[i,0]!=min_index:cluster_changed=True
            #cluster_assment 第i行第1個值是質點的腳標,就是這行數據屬於哪個質點往哪聚
            #若這行數據的質點不等於上面得出的質點,則 cluster_changed爲真\
            #這個while還要在循環一下,循環到判斷不成立爲止
            cluster_assment[i,:]=min_index,min_distance**2
            #讓第i行數據的質點等於上面得到的質點,第二個參數是距離的平方
        print centroids
        #質點是在變換的
        for cent in range(k):
            pts_in_clust=data[nonzero(cluster_assment[:,0].A==cent)[0]]
            #pts_in_clust是數據中質點等於cent且非零的那一行元素所構成的矩陣
            centroids[cent,:]=mean(pts_in_clust,axis=0)
            #更新質點爲(x,y);x=pts_in_clust第一列的平均數;y=pts_in_clust第二列的平均數
            #這一步也就是k均值聚類的核心,所謂的均值
            #就是說質點是接近於這一類數據的平均數,想想圓心就明白了
            #各數據到質點的距離要短,每一集羣的點都是到這一質點距離最短的點
    return centroids,cluster_assment
def bisection_kmeans(data,k):
    '''二分k-均值聚類算法'''
    m=shape(data)[0]
    cluster_assment=mat(zeros((m,2)))
    #初始聚類估值矩陣
    centroid0=mean(data,axis=0).tolist()[0]
    #初始質點爲所有數據的平均數.僅一個點
    #數據有兩列,正好兩個平均值構成一個二維點
    #tolist()轉換成列表
    cent_list=[centroid0]
    #質點列表
    for j in range(m):
        #各數據點到預估質點的歐式距離的平方
        #平方是爲了拉開差距,有利於二分。使小於1的越小,大於1的越大
        cluster_assment[j,1]=distance(mat(centroid0),data[j,:])**2
    while (len(cent_list)<k):
        #不應該自定義k
        lowest_sse=inf
        #最低sse
        #sse誤差平方和
        for i in range(len(cent_list)):
            pts_in_curr_cluster=data[nonzero(cluster_assment[:,0].A==i)[0],:]
            #取出非零下預估的質點爲i的數據
            centroid_mat,split_clust_ass=k_means(pts_in_curr_cluster,2,distance)
            #再根據k均值聚類法把上面取出的數據再分下,k設爲2就是爲了二分
            #因爲是二分所以centroid_mat有兩個質點
            sse_after_split=sum(split_clust_ass[:,1])
            #劃分後sse
            #split_clust_ass[:,1]這一列是各數據點到各自質點的距離
            #怎麼纔是最佳聚類,就是讓這個所有的距離和最小
            #以每個數據點爲質點距離和爲0,但就不是聚類的
            #所以最佳的聚類是要質點的數量與距離和之間的平衡點
            sse_before_split=sum(cluster_assment[nonzero(cluster_assment[:,0].A!=i)[0],1])
            #劃分前sse
            #取出估值中質點不等於i的距離值,然後求和
            print 'sse after and before split',sse_after_split,sse_before_split
            if (sse_after_split+sse_before_split)<lowest_sse:
                #以此來判斷是否需要再分下去
                #若劃分前後的數據sse之和小於最低sse
                #則更新最佳劃分質點,最佳質點矩陣,最佳預估結果,最低sse
                best_cent_to_split=i
                best_new_cents=centroid_mat.tolist()
                best_clust_ass=split_clust_ass.copy()
                lowest_sse=sse_after_split+sse_before_split
        best_clust_ass[nonzero(best_clust_ass[:,0].A==1)[0],0]=len(cent_list)
        #最佳預估結果的中質點等於1的更改爲質點len(cent_list)
        #因爲質點列表程增長狀態(二分法)
        best_clust_ass[nonzero(best_clust_ass[:,0].A==0)[0],0]=best_cent_to_split
        #最佳預估結果中質點等於0 的更改爲上面得出的最佳質點,因爲0是默認值
        print 'the best_cent_to_split is :',best_cent_to_split
        print 'the len of best_clust_ass is :',len(best_clust_ass)
        cent_list[best_cent_to_split]=best_new_cents[0]
        #更新質點列表
        cent_list.append(best_new_cents[1])
        cluster_assment[nonzero(cluster_assment[:,0].A==best_cent_to_split)[0],:]=best_clust_ass
    return cent_list,cluster_assment
def test():
    data=load_data('testSet2.txt')
    data=mat(data)
    cent,clus=k_means(data,3)
    cent_list,cluster_assment=bisection_kmeans(data,3)
    s= mat(cent_list)
    #print s
    #print 'centroids',cent
   # print 'cluster',clus
    import matplotlib.pyplot as plt
    fig=plt.figure()
    ax=fig.add_subplot(111)
    x=[float(i) for i in data[:,0]]
    y=[float(i) for i in data[:,1]]
    ax.scatter(x,y)   
    x1=s[:,0]
    y1=s[:,-1]
    #print x1,y1
    x2=cent[:,0]
    y2=cent[:,1]
    ax.plot(x1,y1,'+',c='red')
    ax.plot(x2,y2,'*',c='green')
    plt.show()    
test()


圖中紅色十字爲二分法爲質點,綠星味均值法質點。兩者能得到相同的結果。



樣本數據:

3.275154 2.957587
-3.344465 2.603513
0.355083 -3.376585
1.852435 3.547351
-2.078973 2.552013
-0.993756 -0.884433
2.682252 4.007573
-3.087776 2.878713
-1.565978 -1.256985
2.441611 0.444826
-0.659487 3.111284
-0.459601 -2.618005
2.177680 2.387793
-2.920969 2.917485
-0.028814 -4.168078
3.625746 2.119041
-3.912363 1.325108
-0.551694 -2.814223
2.855808 3.483301
-3.594448 2.856651
0.421993 -2.372646
1.650821 3.407572
-2.082902 3.384412
-0.718809 -2.492514
4.513623 3.841029
-4.822011 4.607049
-0.656297 -1.449872
1.919901 4.439368
-3.287749 3.918836
-1.576936 -2.977622
3.598143 1.975970
-3.977329 4.900932
-1.791080 -2.184517
3.914654 3.559303
-1.910108 4.166946
-1.226597 -3.317889
1.148946 3.345138
-2.113864 3.548172
0.845762 -3.589788
2.629062 3.535831
-1.640717 2.990517
-1.881012 -2.485405
4.606999 3.510312
-4.366462 4.023316
0.765015 -3.001270
3.121904 2.173988
-4.025139 4.652310
-0.559558 -3.840539
4.376754 4.863579
-1.874308 4.032237
-0.089337 -3.026809
3.997787 2.518662
-3.082978 2.884822
0.845235 -3.454465
1.327224 3.358778
-2.889949 3.596178
-0.966018 -2.839827
2.960769 3.079555
-3.275518 1.577068
0.639276 -3.412840


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