from numpy import *
def load_data(file_name):
data=[]
fr=open(file_name)
for line in fr.readlines():
cur_line=line.strip().split('\t')
flt_line=map(float,cur_line)
data.append(flt_line)
return data
def distance(a,b):
'''計算倆向量的歐式距離'''
return sqrt(sum(power(a-b,2)))
def rand_cent(data,k):
n=shape(data)[1]
#n數據的列數
centroids=mat(zeros((k,n)))
#初始質點矩陣,k爲質點數
for j in range(n):
min_j=min(data[:,j])
#選出第j列中最小的數
range_j=float(max(data[:,j])-min_j)
#用j列中最大的減去最小的
centroids[:,j]=min_j+range_j*random.rand(k,1)
#獲得隨機質點,質點在數據範圍之內
return centroids
def k_means(data,k,distance=distance,create_cent=rand_cent):
'''k均值聚類算法'''
m=shape(data)[0]
cluster_assment=mat(zeros((m,2)))
#cluster_assment第一列爲質點,第二列爲距離
centroids=create_cent(data,k)
cluster_changed=True
while cluster_changed:
cluster_changed=False
for i in range(m):
min_distance=inf;min_index=-1
#初始最小距離爲正無窮,最小索引爲-1
for j in range(k):
#j 相當於質點腳標
#k爲4 ,則就有4個質點,j就是每個質點的腳標
dist_ji=distance(centroids[j,:],data[i,:])
#計算每個隨機質點與各數據的距離
if dist_ji<min_distance:
#若隨機質點與數據的距離 ji 小於最小距離,則最小距離就繼承ji
#最小索引爲j
min_distance=dist_ji;min_index=j
if cluster_assment[i,0]!=min_index:cluster_changed=True
#cluster_assment 第i行第1個值是質點的腳標,就是這行數據屬於哪個質點往哪聚
#若這行數據的質點不等於上面得出的質點,則 cluster_changed爲真\
#這個while還要在循環一下,循環到判斷不成立爲止
cluster_assment[i,:]=min_index,min_distance**2
#讓第i行數據的質點等於上面得到的質點,第二個參數是距離的平方
print centroids
#質點是在變換的
for cent in range(k):
pts_in_clust=data[nonzero(cluster_assment[:,0].A==cent)[0]]
#pts_in_clust是數據中質點等於cent且非零的那一行元素所構成的矩陣
centroids[cent,:]=mean(pts_in_clust,axis=0)
#更新質點爲(x,y);x=pts_in_clust第一列的平均數;y=pts_in_clust第二列的平均數
#這一步也就是k均值聚類的核心,所謂的均值
#就是說質點是接近於這一類數據的平均數,想想圓心就明白了
#各數據到質點的距離要短,每一集羣的點都是到這一質點距離最短的點
return centroids,cluster_assment
def bisection_kmeans(data,k):
'''二分k-均值聚類算法'''
m=shape(data)[0]
cluster_assment=mat(zeros((m,2)))
#初始聚類估值矩陣
centroid0=mean(data,axis=0).tolist()[0]
#初始質點爲所有數據的平均數.僅一個點
#數據有兩列,正好兩個平均值構成一個二維點
#tolist()轉換成列表
cent_list=[centroid0]
#質點列表
for j in range(m):
#各數據點到預估質點的歐式距離的平方
#平方是爲了拉開差距,有利於二分。使小於1的越小,大於1的越大
cluster_assment[j,1]=distance(mat(centroid0),data[j,:])**2
while (len(cent_list)<k):
#不應該自定義k
lowest_sse=inf
#最低sse
#sse誤差平方和
for i in range(len(cent_list)):
pts_in_curr_cluster=data[nonzero(cluster_assment[:,0].A==i)[0],:]
#取出非零下預估的質點爲i的數據
centroid_mat,split_clust_ass=k_means(pts_in_curr_cluster,2,distance)
#再根據k均值聚類法把上面取出的數據再分下,k設爲2就是爲了二分
#因爲是二分所以centroid_mat有兩個質點
sse_after_split=sum(split_clust_ass[:,1])
#劃分後sse
#split_clust_ass[:,1]這一列是各數據點到各自質點的距離
#怎麼纔是最佳聚類,就是讓這個所有的距離和最小
#以每個數據點爲質點距離和爲0,但就不是聚類的
#所以最佳的聚類是要質點的數量與距離和之間的平衡點
sse_before_split=sum(cluster_assment[nonzero(cluster_assment[:,0].A!=i)[0],1])
#劃分前sse
#取出估值中質點不等於i的距離值,然後求和
print 'sse after and before split',sse_after_split,sse_before_split
if (sse_after_split+sse_before_split)<lowest_sse:
#以此來判斷是否需要再分下去
#若劃分前後的數據sse之和小於最低sse
#則更新最佳劃分質點,最佳質點矩陣,最佳預估結果,最低sse
best_cent_to_split=i
best_new_cents=centroid_mat.tolist()
best_clust_ass=split_clust_ass.copy()
lowest_sse=sse_after_split+sse_before_split
best_clust_ass[nonzero(best_clust_ass[:,0].A==1)[0],0]=len(cent_list)
#最佳預估結果的中質點等於1的更改爲質點len(cent_list)
#因爲質點列表程增長狀態(二分法)
best_clust_ass[nonzero(best_clust_ass[:,0].A==0)[0],0]=best_cent_to_split
#最佳預估結果中質點等於0 的更改爲上面得出的最佳質點,因爲0是默認值
print 'the best_cent_to_split is :',best_cent_to_split
print 'the len of best_clust_ass is :',len(best_clust_ass)
cent_list[best_cent_to_split]=best_new_cents[0]
#更新質點列表
cent_list.append(best_new_cents[1])
cluster_assment[nonzero(cluster_assment[:,0].A==best_cent_to_split)[0],:]=best_clust_ass
return cent_list,cluster_assment
def test():
data=load_data('testSet2.txt')
data=mat(data)
cent,clus=k_means(data,3)
cent_list,cluster_assment=bisection_kmeans(data,3)
s= mat(cent_list)
#print s
#print 'centroids',cent
# print 'cluster',clus
import matplotlib.pyplot as plt
fig=plt.figure()
ax=fig.add_subplot(111)
x=[float(i) for i in data[:,0]]
y=[float(i) for i in data[:,1]]
ax.scatter(x,y)
x1=s[:,0]
y1=s[:,-1]
#print x1,y1
x2=cent[:,0]
y2=cent[:,1]
ax.plot(x1,y1,'+',c='red')
ax.plot(x2,y2,'*',c='green')
plt.show()
test()
圖中紅色十字爲二分法爲質點,綠星味均值法質點。兩者能得到相同的結果。
樣本數據:
3.275154 2.957587
-3.344465 2.603513
0.355083 -3.376585
1.852435 3.547351
-2.078973 2.552013
-0.993756 -0.884433
2.682252 4.007573
-3.087776 2.878713
-1.565978 -1.256985
2.441611 0.444826
-0.659487 3.111284
-0.459601 -2.618005
2.177680 2.387793
-2.920969 2.917485
-0.028814 -4.168078
3.625746 2.119041
-3.912363 1.325108
-0.551694 -2.814223
2.855808 3.483301
-3.594448 2.856651
0.421993 -2.372646
1.650821 3.407572
-2.082902 3.384412
-0.718809 -2.492514
4.513623 3.841029
-4.822011 4.607049
-0.656297 -1.449872
1.919901 4.439368
-3.287749 3.918836
-1.576936 -2.977622
3.598143 1.975970
-3.977329 4.900932
-1.791080 -2.184517
3.914654 3.559303
-1.910108 4.166946
-1.226597 -3.317889
1.148946 3.345138
-2.113864 3.548172
0.845762 -3.589788
2.629062 3.535831
-1.640717 2.990517
-1.881012 -2.485405
4.606999 3.510312
-4.366462 4.023316
0.765015 -3.001270
3.121904 2.173988
-4.025139 4.652310
-0.559558 -3.840539
4.376754 4.863579
-1.874308 4.032237
-0.089337 -3.026809
3.997787 2.518662
-3.082978 2.884822
0.845235 -3.454465
1.327224 3.358778
-2.889949 3.596178
-0.966018 -2.839827
2.960769 3.079555
-3.275518 1.577068
0.639276 -3.412840