1. 程式人生 > 實用技巧 >關於k-means聚類演算法的原理及解析

關於k-means聚類演算法的原理及解析

一、k-means演算法思想:

第一步,從檔案中讀取資料,點用元組表示,點集用列表表示。
第二步,初始化聚類中心。首先獲取資料的長度,然後在range(0,length)這個區間上隨機產生k個不同的值,以此為下標提取出資料點,將它們作為聚類初始中心,產生列表center。
第三步,分配資料點。將資料點分配到距離(歐式距離)最短的聚類中心中,產生列表assigment,並計算平均誤差。
第四步,如果首次分配後有結果為空,則重新初始化聚類中心。
第五步,更新聚類中心,(計算每一簇中所有點的平均值),然後再次進行分配,並計算平均誤差。
第六步,比較前後兩次的平均誤差是否相等,若不相等則進行迴圈,否則終止迴圈,進入下一步
最少進行兩次聚類,對比誤差,輸出較小誤差時的結果,避免平均誤差過大。

二、流程圖:

三、 結果輸出:

四、散點圖:

五、python實現程式碼:

  1 import random
  2 from math import *
  3 import matplotlib.pyplot as plt
  4  
  5 #從檔案種讀取資料
  6 def read_data():
  7     data_points = []
  8     with open('data.txt','r') as fp:
  9         for line in fp:
 10             if line =='\n':
 11
continue 12 data_points.append(tuple(map(float,line.split(' '))))#去掉空格,並將data中資料的型別轉為tuple 13 fp.close() 14 return data_points 15 16 #初始化聚類中心 17 def begin_cluster_center(data_points,k): 18 center=[] 19 length=len(data_points)#長度 20 rand_data = random.sample(range(0,length), k)#
生成k個不同隨機數 21 for i in range(k):#得出k個聚類中心(隨機選出) 22 center.append(data_points[rand_data[i]]) 23 return center 24 25 #計算最短距離(歐式距離) 26 def distance(a,b): 27 length=len(a) 28 sum = 0 29 for i in range(length): 30 sq = (a[i] - b[i]) ** 2 31 sum += sq 32 return sqrt(sum) 33 #分配樣本 34 # 按照最短距離將所有樣本分配到k個聚類中心中的某一個 35 def assign_points(data_points,center,k): 36 assignment=[] 37 for i in range(k): 38 assignment.append([]) 39 for point in data_points: 40 min = 10000000 41 flag = -1 42 for i in range(k): 43 value=distance(point,center[i])#計算每個點到聚類中心的距離 44 if value<min: 45 min=value#記錄距離的最小值 46 flag=i #記錄此時聚類中心的下標 47 assignment[flag].append(point) 48 return assignment 49 50 #更新聚類中心,計算每一簇中所有點的平均值 51 def update_cluster_center(center,assignment,k): 52 for i in range(k):#assignment中的每一簇 53 x=0 54 y=0 55 length=len(assignment[i])#每一簇的長度 56 if length!=0: 57 for j in range(length): # 每一簇中的每個點 58 x += assignment[i][j][0] # 橫座標之和 59 y += assignment[i][j][1] # 縱座標之和 60 center[i] = (x / length, y / length) 61 return center 62 63 #計算平方誤差 64 def getE(assignment,center): 65 sum_E=0 66 for i in range(len(assignment)): 67 for j in range(len(assignment[i])): 68 sum_E+=distance(assignment[i][j],center[i]) 69 return sum_E 70 71 #計算各個聚類中心的新向量,更新距離,即每一類中每一維均值向量。 72 # 然後再進行分配,比較前後兩個聚類中心向量是否相等,若不相等則進行迴圈, 73 # 否則終止迴圈,進入下一步。 74 def k_means(data_points,k): 75 # 由於初始聚類中心是隨機選擇的,十分影響聚類的結果,聚類可能會出現有較大誤差的現象 76 # 因此如果由初始聚類中心第一次分配後有結果為空,重新選擇初始聚類中心,重新再聚一遍,直到符合要求 77 while 1: 78 # 產生初始聚類中心 79 begin_center = begin_cluster_center(data_points, k) 80 # 第一次分配樣本 81 assignment = assign_points(data_points, begin_center, k) 82 for i in range(k): 83 if len(assignment[i]) == 0:#第一次分配之後有結果為空,說明聚類中心沒選好,重新產生初始聚類中心 84 continue 85 break 86 #第一次的平方誤差 87 begin_sum_E=getE(assignment,begin_center) 88 # 更新聚類中心 89 end_center = update_cluster_center(begin_center, assignment, k) 90 # 第二次分配樣本 91 assignment = assign_points(data_points, end_center, k) 92 # 第二次的平方誤差 93 end_sum_E = getE(assignment, end_center) 94 count = 2 # 計數器 95 #比較前後兩個聚類中心向量是否相等 96 #print(compare(end_center,begin_center)==False) 97 while( begin_sum_E != end_sum_E): 98 begin_center=end_center 99 begin_sum_E=end_sum_E 100 # 再次更新聚類中心 101 end_center = update_cluster_center(begin_center, assignment, k) 102 # 進行分配 103 assignment = assign_points(data_points, end_center, k) 104 #計算誤差 105 end_sum_E = getE(assignment, end_center) 106 count = count + 1 #計數器加1 107 return assignment,end_sum_E,end_center,count 108 def print_result(count,end_sum_E,k,assignment): 109 # 列印最終聚類結果 110 print('經過', count, '次聚類,平方誤差為:', end_sum_E) 111 print('---------------------------------分類結果---------------------------------------') 112 for i in range(k): 113 print('',i+1,'類資料:',assignment[i]) 114 print('--------------------------------------------------------------------------------\n') 115 116 def plot(k, assignment,center): 117 #初始座標列表 118 x = [] 119 y = [] 120 for i in range(k): 121 x.append([]) 122 y.append([]) 123 # 填充座標 並繪製散點圖 124 for j in range(k): 125 for i in range(len(assignment[j])): 126 x[j].append(assignment[j][i][0])# 橫座標填充 127 for i in range(len(assignment[j])): 128 y[j].append(assignment[j][i][1])# 縱座標填充 129 plt.scatter(x[j], y[j],marker='o') 130 plt.scatter(center[j][0], center[j][1],c='b',marker='*')#畫聚類中心 131 # 設定標題 132 plt.title('K-means Scatter Diagram') 133 # 設定X軸標籤 134 plt.xlabel('X') 135 # 設定Y軸標籤 136 plt.ylabel('Y') 137 # 顯示散點圖 138 plt.show() 139 140 def main(): 141 # 3個聚類中心 142 k = 4 143 data_points =read_data() 144 assignment, end_sum_E, end_center, count = k_means(data_points, k) 145 min_sum_E = 1000 146 #返回較小誤差 147 while min_sum_E>end_sum_E: 148 min_sum_E=end_sum_E 149 assignment, end_sum_E, end_center, count = k_means(data_points,k) 150 print_result(count, min_sum_E, k, assignment)#輸出結果 151 plot(k, assignment,end_center)#畫圖 152 main()

data檔案資料:

2.0 4.2
2.1 5.0
2.3 3.8
1.2 6.1
3.5 4.4
3.6 3.7
3.0 6.2
2.5 5.6
1.7 3.3
0.9 6.0
0.4 4.1
7.0 10.0
8.0 9.0
8.2 8.2
9.3 7.9
7.4 7.1
6.8 8.0
8.1 9.5
8.5 10.4
7.6 8.5
8.6 7.3
7.7 8.8
1.8 10.9
1.5 9.5
1.7 8.7
1.3 9.0
2.8 9.6
2.2 9.9
2.1 10.5
2.5 9.2
3.1 10.4
3.9 9.5
3.7 10.4
2.6 8.2
2.8 9.2
2.2 9.3
3.4 8.5
8.1 1.3
8.5 2.4
8.8 3.3
9.0 1.2
10.7 2.9
7.9 3.7
7.5 2.5
8.5 0.6
10.8 1.6
9.2 0.5
6.8 2.0
8.5 3.6
10.0 1.7