天天看點

python簡單實作k-means聚類算法

我對聚類算法的了解:将一堆,無劃分的資料,通過它們之間的相似度進行劃分。(簡單粗暴^。^)

根據上面的了解,K-means算法就能知名曉意了:就是将一堆無劃分的樣本資料,定義需要劃分為K堆,然後通過每個樣本資料點與中心點間的距離進行歸簇。(在k-means中需要在劃分前需指定中心點,這是它的缺點)

下面是官方一點的說法:

K-Means算法是最為經典的基于劃分的聚簇方法,是十大經典資料挖掘算法之一。簡單的說K-Means就是在沒有任何監督信号的情況下将資料分為K份的一種方法。聚類算法就是無監督學習中最常見的一種,給定一組資料,需要聚類算法去挖掘資料中的隐含資訊。聚類算法的應用很廣:顧客行為聚類,google新聞聚類等。K值是聚類結果中類别的數量。

其實k-means算法真的簡單,先不說代碼,就說原理,你隻要仔細了解一遍,用自己的語言總結一下,慢慢就能摸索出代碼。我剛學時參考了一篇部落格,找不到連接配接了,很感謝他寫的很簡單,了解的很快,過了一段時間用自己的思路将代碼摸索了出來。直接看完整代碼:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt

global K
global N
global spot
global unite
global mean
global count

def cluster():  #劃分每個點一個到簇
    global unite
    unite = []      #這裡千萬注意,需要每次都定義,否則會重複添加長度。當下一次運作cluster函數時才會重置unite清單
    for i in range(K):
        unite.append([])
    for i in range(len(spot)):
        max_max = np.iinfo(np.int32).max  #預定義一個極大值,用于比較
        flag = -1
        for j in range(K):
            n = (spot[i][0]-mean[j][0])**2+(spot[i][1]-mean[j][1])**2
            if(n<max_max):
                max_max = n
                flag = j
        unite[flag].append(spot[i])
    for i in range(K):
        print("第{}簇:{}".format(i,unite[i]))

def square_d():
    global count
    sum_E = 0
    for i in range(len(unite)):
        for j in range(len(unite[i])):
            sum_E += ((unite[i][j][0]-mean[i][0])**2 + (unite[i][j][1]-mean[i][1])**2)
    count +=1
    return sum_E

def remean():
    for i in range(len(unite)):
        sum_x = 0
        sum_y = 0
        for j in range(len(unite[i])):
            sum_x += unite[i][j][0]
            sum_y += unite[i][j][1]
        # if(len(unite[i]) == 0):     #分母不為0,如果前面判斷了中心值不一樣可以省略
        #     mean[i] = (0,0)
        # else:
        #     mean[i] = (sum_x/len(unite[i]),sum_y/len(unite[i]))
        mean[i] = (sum_x/len(unite[i]),sum_y/len(unite[i]))    #根據均值,重新定義的中心值

def show():
     #樣本資料橫坐标清單
    xx = []
    for i in range(K):
        xx.append([])
    #樣本資料縱坐标清單
    yy = []
    for i in range(K):
        yy.append([])
    #使用樣本坐标,繪制散點圖
    for i in range(K):
        for j in range(len(unite[i])):
            xx[i].append(unite[i][j][0])
        for j in range(len(unite[i])):
            yy[i].append(unite[i][j][1])
        plt.scatter(xx[i],yy[i],label=i)
        plt.scatter(mean[i][0],mean[i][1],label=i)
        plt.legend()
    #顯示散點圖
    plt.show()

if __name__=="__main__":
    K = 4   #聚類數    手動設定
    N = 30  #樣本數    手動設定
    spot = []   #樣本清單
    for i in range(N):
        spot.append((np.random.randint(50),np.random.randint(50)))  #50是我給它定義的範圍

    unite = []  #每個點歸屬簇
    mean = []   #每個簇的中心值

    set_t = set()
    while(1):           #雖然兩行代碼就可以搞定這個選值,但是我建議使用集合,防止重複值
        d = np.random.randint(N)    #當然如果你有更簡單的方法除外,就我覺得使用集合比較簡單
        set_t.add(d)
        if(len(set_t)==K):
            for i in set_t:
                mean.append(spot[i])
            break

    count = 0
    m = []
    for i in range(2):      #首先兩次循環,得出兩個比較的平方誤差
        cluster()       #聚類
        temp = square_d()   #計算平方誤差
        m.append(temp)
        print("第{}次聚類平方的內插補點為:{}".format(count,temp))
        remean()    #重定義中心值

    while(m[0] != m[1]):    #就兩次循環後,重複選中心值,直至相等
        m[0] = m[1]
        cluster()
        m[1] = square_d()
        print("第{}次聚類平方的內插補點為:{}".format(count,m[1]))
        remean()

    show()  #構散點圖    構圖與k-means無關,但是利用圖表将資料展示出來更直覺、更有說服力
           
python簡單實作k-means聚類算法

這算是寫的很簡單的了,稍微複雜點的,定義每個部分的類于不同檔案,在每個類中更細緻的實作方法,再互相調用執行個體。

這裡我根據代碼總結了實作聚類的思想:

1:随機選擇K個中心值

2:根據樣本資料各點與中心點的距離來進行歸簇

3:通過整個簇的均值,重置每個簇的中心值

4:重複2、3,直至達到最大的疊代次數(例如:我這裡的條件設定為本次與上次的平方誤差相等)

适用範圍及缺陷:

K-Menas算法嘗試找到使平方誤差準則函數最小的簇。當潛在的簇形狀是凸面的,簇與簇之間差別較明顯,且簇大小相近時,其聚類結果較理想。對于處理大資料集合,該算法非常高效,且伸縮性較好。

但該算法除了要事先确定簇數K和對初始聚類中心敏感外,經常以局部最優結束,同時對“噪聲”和孤立點敏感,并且該方法不适于發現非凸面形狀的簇或大小差别很大的簇。

克服缺點的方法:使用盡量多的資料;使用中位數代替均值來克服outlier的問題。

優缺點是借鑒别人的,感覺比我總結的好那麼一點點。是以記住上面的步驟,就算忘記了模闆,也能自己慢慢摸索出來。