基於numpy實現的簡單k-means,k-means淺析
阿新 • • 發佈:2021-01-29
基於numpy實現的簡單k-means
該文從簡單的資料角度講解基於k-means和基於numpy實現k-means程式碼。
文章目錄
前言
k-means是什麼,它的做什麼的,它與先前所說的聚類有什麼關係。
一、 k-means
上一篇文章已經詳解了聚類,那麼基礎應該已經沒有什麼問題了,k-means其實就是用中文來說的話就是k均值聚類,它屬於聚類中的"基於原型的聚類"
基於原型的聚類:假設聚類結構能通過一組原型刻畫,通常情況下,演算法先對原型進行初始化,繼而對原型進行不斷的更新迭代。而不同的更新方式和不同的求解將產生不同的演算法。
k均值演算法
官方定義:給定樣本集D={x1,x2,…,xn};所謂k均值演算法,是求得針對聚類所得簇劃分C={C1,C2…,Ck}最小化的平方誤差,如下
其中:
那麼μi就可以看為是x集合也就是Ci的均值向量。直觀的來看,E在一定程度上刻畫了簇內的所有樣本圍繞簇均值向量的緊密程度。E越小則,越緊密,意味著簇內相似度越高。我們只需要求最小E即可。
接下來我們解析官方這些概念和公式:
從概念入手,我們知道最後是要把資料集劃分為K個簇,那麼"均值"這個倆個字,也很容易理解,從公式入手,所謂的均值其實指的就是均值向量了。那麼如果要實現該演算法的話,我們只需要幾步:
- 隨機選取k個質心,因為要求有k個簇。
- 接下來求解每個樣本到所有質心的距離,並得到最小距離,將該樣本歸入該質心屬於的簇之中
- 計算簇均值向量
- 判斷簇均值向量是否與上次確定的簇均值向量相等,若相等,則返回簇若不相等,則繼續迭代,同時設定迭代上限,防止沒有結果的崩潰。
從理論上來說,該方法,適用於所有的資料集,無論是有序屬性還是無序屬性,需要變更的僅僅是距離的演算法問題罷了。
- 顯然不難理解,這是k-means的優點之一,同時它還擁有了收斂速度快,聚類效果優,調參簡單等優點。
二、程式碼原理實現
1.引入庫
程式碼如下(示例):
#基於numpy的K-means
#coding:UTF-8
#倒庫
import numpy as np
import random as rd
import matplotlib.pyplot as plt
from math import *
2.面向物件實現
class k_m():
#計算距離 返回歐氏距離 這裡僅僅考慮到了有序屬性
def __init__(self):
self.color=['.b','.g','.r','.c','.m','.y','.k','.w']
self.cencolor=['*b','*g','*r','*c','*m','*y','*k','*w']
#0表示當前顏色的索引
self.now=0
def dist(x,y):
return sqrt(pow(x[0]-y[0],2)+pow(x[1]-y[1],2))
#crowd_count:簇的個數,也是定義K_means點的個數,傳入x座標和y座標,
def k_means(self,x,y,k):
#獲取含有k個隨機屬性的屬性序列
k_list=[(x[i],y[i]) for i in rd.sample(range(len(x)),k)]
#對屬性排序,以便處理
k_list.sort()
while 1:
#crowds為簇列表,每一次遍歷都要更新簇列表
crowds=[[] for i in range(k)]
for i in range(len(x)):
t_now=(x[i],y[i])
#計算到所有點的距離 存列表 接下來我們需要該點到所有質心的距離最近並且將其加入簇列表中
all_dist=[k_m.dist(k_list[j],t_now) for j in range(k)]
min_dist=all_dist.index(min(all_dist))
#加入索引之中
crowds[min_dist].append(i)
#進入質心的迭代
centroid_news_list=[]
#從之前得到的簇中計算均值,重新確定質心並進行運算
for i in range(k):
x_t=sum(x[j] for j in crowds[i])/len(crowds[i])
y_t=sum(y[j] for j in crowds[i])/len(crowds[i])
centroid_news_list.append((x_t,y_t))
centroid_news_list.sort()
#繪圖
plt.figure()
plt.title('k={0:d} k-means'.format(k))
for i in range(k):
#繪點
plt.plot([x[j] for j in crowds[i]],[y[j] for j in crowds[i]],self.color[self.now])#
#繪質心
plt.plot(k_list[i][0],k_list[i][1],self.cencolor[self.now])#
self.now+=1
plt.show()
plt.close('all')
if centroid_news_list==k_list:
return crowds
else:
self.now=0
k_list=centroid_news_list
- 看完程式碼之後不難發現,該程式碼非常簡單,但同樣,其處理能力也有限,因為該程式碼只是為了從原理角度淺析k-means演算法,因此不會用於處理很複雜的資料,但是是其對二元組資料處理仍然十分優秀,這邊博主已經用資料集測試過了,程式碼沒有bug可以放心參考。
總結
k-means本身作為原型聚類中最著名的劃分聚類演算法,以及最為廣泛使用的劃分聚類演算法,其優秀程度自然是不言而喻。有興趣的小夥伴可以嘗試用無序屬性的資料集,以及無序距離的計算處理複雜任務,比如物品的分類等等,實現也沒有很難,但是十分有用。