# k-means 筆記 ```python= import random import matplotlib.pyplot as plt import numpy as np #距離比較,並存取哪個點離中心點近存取 def distance_cmp(num_k, b_data, k_center): #num_k: 中心點座標, b_data: 原始資料, k_center: 欲算的k值 x_cluster = [[] for i in range(k_center)] #存取群聚的點 for i in range(len(b_data)): Dis = [] for j in range(k_center): sum = 0 for k in range(4): #(4維值) sum = sum + (num_k[j][k] - b_data[i][k]) ** 2 sum = sum ** 0.5 Dis.append(sum) for dis_index in range(k_center): if(Dis[dis_index] == min(Dis)): x_cluster[dis_index].append(i) #取最小值抓入 for i in range(k_center): final_cluster_point[i] = x_cluster[i] print("No.", i + 1, "cluster :", final_cluster_point[i]) update_cmp(x_cluster, b_data, k_center) #更新群心點座標 #更新群心點座標 def update_cmp(c_data, b_data, k_center): #c_data: 每個群的點, b_data: 原始資料, k_center: 欲算的k值 x_update = [[] for i in range(k_center)] for j in range(k_center): for i in range(4): sum = 0 for k in c_data[j]: sum = sum + (b_data[k][i]) x_update[j].append(sum / len(c_data[j])) #更新群心點座標 for i in range(k_center): x[i] = x_update[i] print("No.", i + 1, "x_update :", x[i]) #算出最佳k值解(SSE) def sum_squared_error(stable_data, source_data, cluster_data, k_num): sum_all_point_dis = 0 for num_cluster in range(k_num): for i in cluster_data[num_cluster]: sum_dis = 0 for j in range(4): sum_dis = sum_dis + (stable_data[num_cluster][j] - source_data[i][j]) ** 2 sum_dis = sum_dis ** 0.5 sum_all_point_dis = sum_all_point_dis + sum_dis optimal_k.append(sum_all_point_dis) optimal_k = [] for k in range(1, 10): data = np.loadtxt('k-means_data.txt', dtype = np.int32) x = [[0] * 4 for i in range(k)] final_cluster_point = [[] for i in range(k)] y = range(len(data)) index = random.sample(y, k) for i in range(k): x[i] = data[index[i]] for i in range(k): print("No.", i + 1, "k =", x[i]) print("\n") x_stable = [[] * 4 for i in range(k)] for i in range(k): for j in range(4): x_stable[i].append(x[i][j]) print("Iteration 1") distance_cmp(x, data, k) print("\n") #迭代上限77 for iteration in range(77): #判斷是否迭代完有更新 if(x_stable != x): x_stable = [[] * 4 for i in range(k)] for i in range(k): for j in range(4): x_stable[i].append(x[i][j]) print("Iteration ", iteration + 2) distance_cmp(x, data, k) print("\n") else : break sum_squared_error(x_stable, data, final_cluster_point, k) print(optimal_k) n_k = [1, 2, 3, 4, 5, 6, 7, 8, 9] plt.xlabel('k') plt.ylabel('SSE') plt.plot(n_k, optimal_k) plt.show() ``` ### 從這開始講解 ```python= def distance_cmp(num_k, b_data, k_center): #num_k: 中心點座標, b_data: 原始資料, k_center: 欲算的k值 x_cluster = [[] for i in range(k_center)] #存取群聚的點 for i in range(len(b_data)): Dis = [] for j in range(k_center): sum = 0 for k in range(4): #(4維值) sum = sum + (num_k[j][k] - b_data[i][k]) ** 2 sum = sum ** 0.5 Dis.append(sum) for dis_index in range(k_center): if(Dis[dis_index] == min(Dis)): x_cluster[dis_index].append(i) #取最小值抓入 for i in range(k_center): final_cluster_point[i] = x_cluster[i] print("No.", i + 1, "cluster :", final_cluster_point[i]) update_cmp(x_cluster, b_data, k_center) #更新群心點座標 ``` - 假設目前資料有 10 筆, 想要找到最佳分群數目 最爛就是10群 最好1群, 所以可以用窮舉法 用 k_center(1~10)去算 - 現在去算 從 k_center = 1開始算 到 10(隨機)選一個數當中心點 用這個點去與其他點算兩點距離(舉例兩個4維座標 A(x1, y1, z1, k1), B(x2, y2, z2, k2) 可得出 兩點距離公式 $$ sqrt{(x1-x2)^2 + (y1-y2)^2 + (z1-z2)^2 + (k1-k2)^2}) $$ - 就是上方程式中第 9~10行 最後存近一個 Dis = [] 的list #### 接著上方程式中第 3~13行 - 第3行 由於for 迴圈 i 是歷遍所有資料點 10 筆 所以 每筆點都有一個 Dis = [] , 在第5行時 當 k_center = 1 時 Dis = [] 裡面只會有1筆資料, k_center = 2 時 Dis = [] 裡面只會有2筆資料, 此 Dis 存放的資料為此點距離 k_center 中心點的距離 - 舉例 當k_center = 2時 中心點為 A 跟 B, 此時 C 的 $$ Dis = ((C-A的距離), (C-B的距離)) $$ #### 第11行 當 k_center = 2 開始 - ```python= for dis_index in range(k_center) ``` - for迴圈 dis_index 會 從 0~1 對應著上述舉例 (C-A的距離) 與 (C-B的距離), min(Dis) 則是返回 最短距離那個 找出 C離 A還是B哪個近 ```python= x_cluster[dis_index].append(i) #取最小值抓入 ``` - C 最靠近哪個中心點則放進那個群體 **x_cluster[dis_index]**, i 是 C 那個點的index ```python= update_cmp(x_cluster, b_data, k_center) #更新群心點座標 ``` - 這行功能是對那個群體做中心點的更新 ```python= #更新群心點座標 def update_cmp(c_data, b_data, k_center): #c_data: 每個群的點, b_data: 原始資料, k_center: 欲算的k值 x_update = [[] for i in range(k_center)] for j in range(k_center): for i in range(4): #(4維值) sum = 0 for k in c_data[j]: sum = sum + (b_data[k][i]) x_update[j].append(sum / len(c_data[j])) #更新群心點座標 for i in range(k_center): x[i] = x_update[i] print("No.", i + 1, "x_update :", x[i]) ``` - 先定義更新後的中心點座標 ```python= x_update = [[] for i in range(k_center)] ``` - 更新群心點座標顧名思義 選取那個群體的所有點取中心, 假設現在有3個4維的點 A(x1, y1, z1, k1), B(x2, y2, z2, k2), C(x3, y3, z3, k3) $$ 中心sum(sumx, sumy, sumz, sumk) =(((x1 + x2 + x3)/3),((y1 + y2 + y3) / 3),((z1 + z2 + z3) / 3),((k1 + k2 + k3) / 3)) $$ ```python= sum / len(c_data[j]) ``` - for k in c_data[j]: 中的 k 是歷遍 c_data[j] 此群體裡面的值, 而這個值代表所有資料點的 那個 index 位置 ### 此處要注意 更新完後的群心點座標後 要在帶入回 distance_cmp 這個function 等到 中心點不在更新後 確認群體的中心點 ```python= #最終算出最佳k值解(SSE) def sum_squared_error(stable_data, source_data, cluster_data, k_num): sum_all_point_dis = 0 for num_cluster in range(k_num): for i in cluster_data[num_cluster]: sum_dis = 0 for j in range(4): sum_dis = sum_dis + (stable_data[num_cluster][j] - source_data[i][j]) ** 2 sum_dis = sum_dis ** 0.5 sum_all_point_dis = sum_all_point_dis + sum_dis optimal_k.append(sum_all_point_dis) ``` - stable_data 為最終穩定的中心點, source_data 所有資料點, cluster_data 群體, k_num 為 幾個群心 - SSE 算出穩定中心點與它群體的所有點距離合 作為分幾群的評判指標 - 資料如果為n筆資料 則 迭代 n 次 - 一直重複 distance_cmp 與 update_cmp function 最終找到穩定不在更新的群體中心點