kmeans手工實現

2021-09-17 18:32:10 字數 4499 閱讀 7939

演算法原理

kmeans的計算方法如下:

1 隨機選取k個中心點

2 遍歷所有資料,將每個資料劃分到最近的中心點中

3 計算每個聚類的平均值,並作為新的中心點

4 重複n次,直到這k個中心點不再變化(收斂了),或執行了足夠多的迭代

**實現:

import math

import random

class cluster(object):

"""聚類

"""def __init__(self, samples):

if len(samples) == 0:

# 如果聚類中無樣本點

raise exception("錯誤:乙個空的聚類!")

# 屬於該聚類的樣本點

self.samples = samples

# 該聚類中樣本點的維度

self.n_dim = samples[0].n_dim

# 判斷該聚類中所有樣本點的維度是否相同

for sample in samples:

if sample.n_dim != self.n_dim:

raise exception("錯誤: 聚類中樣本點的維度不一致!")

# 設定初始化的聚類中心

self.centroid = self.cal_centroid()

def update(self, samples):

"""計算之前的聚類中心和更新後聚類中心的距離

"""old_centroid = self.centroid

self.samples = samples

self.centroid = self.cal_centroid()

shift = get_distance(old_centroid, self.centroid)

return shift

def cal_centroid(self):

"""對於一組樣本點計算其中心點

"""n_samples = len(self.samples)

# 獲取所有樣本點的座標(特徵)

coords = [sample.coords for sample in self.samples]

print('coords',coords)

unzipped = zip(*coords)

# 計算每個維度的均值

centroid_coords = [math.fsum(d_list)/n_samples for d_list in unzipped]

return sample(centroid_coords)

class sample(object):

"""樣本點類

"""def __init__(self, coords):

self.coords = coords # 樣本點包含的座標

self.n_dim = len(coords) # 樣本點維度

def get_distance(a, b):

if a.n_dim != b.n_dim:

# 如果樣本點維度不同

raise exception("錯誤: 樣本點維度不同,無法計算距離!")

acc_diff = 0.0

for i in range(a.n_dim):

square_diff = pow((a.coords[i]-b.coords[i]), 2)

acc_diff += square_diff

distance = math.sqrt(acc_diff)

return distance

def gen_random_sample(n_dim, lower, upper):

"""生成隨機樣本

"""sample = sample([random.uniform(lower, upper) for _ in range(n_dim)])

return sample

以上為定義kmeans函式需要使用的工具,即包含了資料的類物件,距離計算,收斂情況計算,樣本生成

(1)資料   samples

(2)聚類的個數   k

(3)收斂的閾值   cutoff

def kmeans(samples, k, cutoff):

"""kmeans函式

"""# 隨機選k個樣本點作為初始聚類中心

init_samples = random.sample(samples, k)

# 建立k個聚類,聚類的中心分別為隨機初始的樣本點

clusters = [cluster([sample]) for sample in init_samples]

print('clusters',clusters)

# 迭代迴圈直到聚類劃分穩定

n_loop = 0

while true:

# 初始化一組空列表用於儲存每個聚類內的樣本點

lists = [ for _ in clusters]

for _ in clusters:

print(_)

# 開始迭代

n_loop += 1

# 遍歷樣本集中的每個樣本

for sample in samples:

# 計算樣本點sample和第乙個聚類中心的距離

smallest_distance = get_distance(sample, clusters[0].centroid)

# 初始化屬於聚類 0

cluster_index = 0

# 計算和其他聚類中心的距離

for i in range(k - 1):

# 計算樣本點sample和聚類中心的距離

distance = get_distance(sample, clusters[i+1].centroid)

# 如果存在更小的距離,更新距離

if distance < smallest_distance:

smallest_distance = distance

cluster_index = i + 1

# 找到最近的聚類中心,更新所屬聚類

# 初始化最大移動距離

biggest_shift = 0.0

# 計算本次迭代中,聚類中心移動的距離

for i in range(k):

shift = clusters[i].update(lists[i])

# 記錄最大移動距離

biggest_shift = max(biggest_shift, shift)

# 如果聚類中心移動的距離小於收斂閾值,即:聚類穩定

if biggest_shift < cutoff:

print("第{}次迭代後,聚類穩定。".format(n_loop))

break ## 跳出while迴圈

# 返回聚類結果

return clusters

def run_main():

"""主函式

"""# 樣本個數

n_samples = 1000

# 特徵個數 (特徵維度)

n_feat = 2

# 特徵數值範圍

lower = 0

upper = 200

# 聚類個數

n_cluster = 5

# 生成隨機樣本

samples = [get_random_sample(n_feat, lower, upper) for _ in range(n_samples)]

#print(samples)

# 收斂閾值

cutoff = 0.2

clusters = kmeans(samples, n_cluster, cutoff)

#輸出結果

for i, c in enumerate(clusters):

for sample in c.samples:

('cluster:',clusters)

print('聚類--{},樣本點--{}'.format(i, sample))

# 視覺化結果

plt.subplot()

# color_names = list(mcolors.cnames)

for i, c in enumerate(clusters):

x =

y =

# random.choice

# color = [color_names[i]] * len(c.samples)

for sample in c.samples:

plt.scatter(x, y)

plt.show()

if __name__ == '__main__':

run_main()

手工實現LinkedList

參照其底層 按照自己的理解實現了linkedlist的一些基本功能。如果對c和c 指標了解一下,理解起來非常快。package cn.liu.mylinkedlist 結點 public class node 構造器,來傳資料 public node object element package c...

手工實現linkedList

鍊錶結構就像一根鏈條一樣,環環相扣。每一環 node entry 都由next previous,element 存放資料的地方 第乙個的next 是第二個,第二個的next是第三個,直到最後乙個的next 可以為null 最後第乙個的previous 是最後第二個,最後第二個的previous是最...

手工實現ArrayList

思路 我們知道arraylist底層是陣列,所以當我們建立arraylist物件時就是呼叫空構造器,讓構造器來幫我們建立好乙個長度為10 自己擬定的長度 的陣列,然後我們可以呼叫內部提供的各種發放來操作arraylist,所以讓我們自己來手寫乙個arraylist。第一步 讓我們由簡單到難分布完成,...