python手動實現K_means聚類(指定K值)

唏噓#發表於2020-11-19

前言

這幾天作業,用到了k_means聚類,作為一名deeper,怎麼能不手動實現一下呢?然後,我就錯過了作業的提交時間。。。。

實現

  • 原理
    簡單來說,可以分為以下幾步:
  1. 指定有幾個簇
  2. 每個簇任選一個點作為初值(資料中的點),中心點(幾個簇就幾個中心點),會有很多種情況
  3. 通過指定的距離函式,計算資料中每個點與中心點的距離
  4. 將計算的點加入到與其最近的簇中
  5. 所有點都進行一遍之後,重新計算中心點(簇中的每個點加起來,求平均值)
  6. 重複以上步驟,直至簇不會變化
  7. 將所有的情況進行計算,找出最小的(也可以通過設定閾值),就是最合適的聚類(當然也有很多評判方式,這裡實現一個最簡單的,通過求出簇中的所有點到中心點的平方均值,補充:除了計算簇內部的值要求最小之外,也可以計算簇與簇之間的值越大。因為最理想的情況就是簇內部越近越好,簇與簇之間越遠越好。)
    在這裡插入圖片描述
#Author:龍文漢
#Data:2020.11.18
#Discription:聚類演算法
import numpy as np
import copy
import itertools

class K_means():
    def __init__(self,data,cu_num):
        self.arry_first = data
        self.cu_num = cu_num
        self.cu = []
        self.arry = []

#初始化每個簇,根據設定的簇數量,隨機新增簇
    def Cu_initial(self,x):
        #x = random.sample(range(0,len(self.arry)-self.cu_num),self.cu_num)
        #print(x)
        # x = np.random.randint(1,6,self.cu_num,"int32")#會重複
        self.arry = copy.deepcopy(self.arry_first)
        #print("^^^^^^^",self.arry)
        self.cu = []
        for i in x:
            self.cu.append([])
            self.cu[-1].append(data[i])
            self.arry.remove(data[i])
            #print(self.arry)
#計算歐氏距離
    def Ou_in(self,data1,data2):
        #print(data1,data2)
        ans = ((data1[0]-data2[0])**2+(data1[1]-data2[1])**2)**0.5
        #print(ans)
        return ans
#計算每個簇的平方和均差
    def Pinfang(self,tem):
        # cu_center = 0
        # for i in cu:
        #     cu_center+=i
        # cu_center /= len(cu)
        # pinfang = 0
        # for i in cu:
        #     pinfang += (cu-cu_center)**2
        pingfang = 0
        for cu in tem:
            cu_center = np.mean(cu,axis=0)
            pingfang += np.sum(list(map(lambda x:self.Ou_in(x,cu_center)**2,cu)))
        return pingfang
#開始進行沒一簇的聚類
    def Evualution(self):
        first = 1
        while(True):#一直迴圈,直到簇不再變化
            center_list = []
            tem = copy.deepcopy(self.cu)  # 暫時簇的形式,便於對後面進行對比,注意複製形式,地址的問題
            if first==0:
                self.cu = []
                self.arry = copy.deepcopy(self.arry_first)
            for i in range(self.cu_num):#計算每一個簇的中心點
                center_list.append(np.mean(tem[i], axis=0))
                if first==0:
                    self.cu.append([])
            if first == 1:
                first = 0
            print("每個簇的中心點",center_list)
            for i in self.arry:#依次對每個點進行迭代計算(根據歐式距離決定歸屬)
                print("開始對該點的聚類",i)
                ans_list = []  # 一個點對於每一個簇的歐式距離集合
                for x in center_list:#依次迭代每個中心簇
                    ans_list.append(self.Ou_in(i, x))#增加一個點對每個簇的歐式距離
                print("該點和所有簇的歐式距離集合",ans_list)
                print("最小的歐式距離為",min(ans_list),"index是",ans_list.index(min(ans_list)))
                self.cu[ans_list.index(min(ans_list))].append(i)#更新簇,將該點新增到歐式距離最小的簇中
                print("新增該節點之後的簇",self.cu)
            print("原來的簇", tem)
            print("更新之後的簇",self.cu)
            if (self.cu == tem):#如果簇已經不變,則說明此時完成了聚類
                return tem
            print("所有的點本輪聚類結束(在這之前的中心點不改變)")
#開始找出最優解
    def Best(self):
        min_list = []
        min_pin = 100000000
        x = list(itertools.permutations(range(len(self.arry_first)),self.cu_num))
        for w in x:
            self.Cu_initial(w)
            tem = self.Evualution()
            pin = self.Pinfang(tem)
            print("本次的平方差值為",pin)
            if pin < min_pin:
                min_pin=pin
                min_list = self.cu
        return min_list,min_pin

if __name__ == "__main__":
    data = [[4.0, 2.5], [1.5, 1.0], [3.0, 1.5], [4.5, 3.5], [4.0, 2.5], [2.5, 5.0]]
    # data.remove([4.0, 2.5])
    # print(data)
    k_means = K_means(data,2)
    answer = k_means.Best()
    print(answer)



參考:K-means原理、優化及應用

相關文章