一、目的意义
内容包括:
(1)问题描述:k-means聚类算法的研究,此算法主要对二维数据点进行聚类。
二、算法阐述(占20%)
—输入:期望得到的簇的数目k,n个对象的二维数据D。
—输出:k个簇的集合。
算法步骤:
(1)输入k及n个二维数据对象
(2)随机选择k个对象作为初始的簇的质心
(3)repeat
(4)计算每个对象与各个簇的质心的距离,将对象划分到距离 其最近的簇
(5)重新计算每个簇的质心并更新
(6)until所有分类下标都等于最小下标,算法停止
(7)输出这k个簇的集合。
三、算法实现(占30%)
[注]:以下代码为算法的主要代码
命名为k_means.py的文件,此文件下的代码为:
from numpy import *
import matplotlib.pyplot as plt
#计算欧氏距离
def euclDistance(vector1, vector2):
return sqrt(sum(power(vector2 - vector1, 2)))
#初始化质心,采用随机产生质心
def initCentroids(dataSet, k):
numSamples, dim = dataSet.shape
#numSamples = 80
#dim = 2
centroids = mat(zeros((k, dim)))
#[[ 0. 0.]
# [ 0. 0.]
# [ 0. 0.]
# [ 0. 0.]]
for i in range(k):
index = int(random.uniform(0, numSamples)) #产生随机数,如果随机数产生相等了,如22,40,22,13,那么显示的聚类将减少,因为有个聚类的结果始终为空
#print(index)
centroids[i, :] = dataSet[index, :]
#print(centroids)
return centroids
#centroids
#[[-2.123337 2.943366]
# [ 2.046259 2.735279]
# [-2.967647 2.848696]
# [ 3.542056 2.778832]]
#centroids, clusterAssment = k_means.kmeans(dataSet, k)
#k-means
def kmeans(dataSet, k):
numSamples = dataSet.shape[0] #计算行数用0,计算列数用1, numSamples=80
clusterAssment = mat(zeros((numSamples, 2))) #80*2的零矩阵
clusterChanged = True
centroids = initCentroids(dataSet, k) #初始化质心
while clusterChanged:
clusterChanged = False
for i in range(numSamples): #循环80行
minDist = 100000.0 #假设当前的这个距离最小
minIndex = 0
for j in range(k): # 求出每一行距离最小的下标,最小下标即是每个点所属类别
distance = euclDistance(centroids[j],dataSet[i])
if distance < minDist:
minDist = distance
minIndex = j
if clusterAssment[i, 0] != minIndex: #对象的下标不等于最小下标,直到所有对象下标都等于最小下标,算法停止。
clusterChange = True
clusterAssment[i, :] = minIndex, minDist**2
#print(clusterAssment[i, :])
#[[ 2. 2.32363089]]
#[[ 1. 0.14270381]]
#[[ 3. 9.63189586]]
for j in range(k): #将同一个簇的点放在一起
pointsInCluster = dataSet[nonzero(clusterAssment[:, 0] == j)[0]]
#print(pointsInCluster)
#print(nonzero(clusterAssment[:, 0] == j))
#(array([ 3, 27, 51, 71, 79]), array([0, 0, 0, 0, 0]))
centroids[j, :] =mean(pointsInCluster, axis = 0) #计算每一列的均值,得出每个簇的质心
#print(centroids) #[[ 1.09078155 -3.17263173]
# [ 2.6265299 3.10868015]
# [ 4.589752 -1.575316 ]
# [-3.03458403 0.53734279]]
return centroids, clusterAssment
def showCluster(dataSet, k, centroids, clusterAssment):
numSamples, dim = dataSet.shape
if dim != 2:
print("Sorry! I can not draw because the dimension of your data is not 2!")
return 1
#设定普通点颜色形状
mark = ['or','ob','og','ok','^r','+r','sr','dr','<r','pr']
if k > len(mark):
print("Sorry! Your k is too large!")
return 1
## 画出所有样例点 属于同一分类的绘制同样的颜色
for i in range(numSamples):
markIndex = int(clusterAssment[i, 0])
plt.plot(dataSet[i, 0], dataSet[i, 1], mark[markIndex])
#设定质心颜色形状
mark = ['Dr', 'Db', 'Dg', 'Dk', '^b', '+b', 'sb', 'db', '<b', 'pb']
#画出质心
for i in range(k):
plt.plot(centroids[i, 0], centroids[i, 1], mark[i], markersize = 12)
plt.show()
命名为test_kmeans.py的文件,此文件下的代码为:
import matplotlib matplotlib.use('Tkagg') #要想绘图成功,必须加上这两句,也可修改matplotlib的配置文件, #则不用这两句 from numpy import * #numpy 是python的函数库, import matplotlib.pyplot as plt import k_means ##处理数据,dataSet为嵌套列表[[1.658985, 4.285136], [-3.453687, 3.424321],.....] print("loading data...") dataSet = [] fileIn = open('testSet.txt') for line in fileIn.readlines(): lineArr = line.strip().split() #['1.658985', '4.285136'] #['-3.453687', '3.424321'] dataSet.append([float(lineArr[0]), float(lineArr[1])]) #dataSet 嵌套列表 print("clustering..." ) dataSet = mat(dataSet) ####转成80*2的矩阵 k = 4 centroids, clusterAssment = k_means.kmeans(dataSet, k) print("show the result...") k_means.showCluster(dataSet, k, centroids, clusterAssment)
测试数据文件,文件名为testSet.txt
1.658985 4.285136
-3.453687 3.424321
4.838138 -1.151539
-5.379713 -3.362104
0.972564 2.924086
-3.567919 1.531611
0.450614 -3.302219
-3.487105 -1.724432
2.668759 1.594842
-3.156485 3.191137
3.165506 -3.999838
-2.786837 -3.099354
4.208187 2.984927
-2.123337 2.943366
0.704199 -0.479481
-0.392370 -3.963704
2.831667 1.574018
-0.790153 3.343144
2.943496 -3.357075
-3.195883 -2.283926
2.336445 2.875106
-1.786345 2.554248
2.190101 -1.906020
-3.403367 -2.778288
1.778124 3.880832
-1.688346 2.230267
2.592976 -2.054368
-4.007257 -3.207066
2.257734 3.387564
-2.679011 0.785119
0.939512 -4.023563
-3.674424 -2.261084
2.046259 2.735279
-3.189470 1.780269
4.372646 -0.822248
-2.579316 -3.497576
1.889034 5.190400
-0.798747 2.185588
2.836520 -2.658556
-3.837877 -3.253815
2.096701 3.886007
-2.709034 2.923887
3.367037 -3.184789
-2.121479 -4.232586
2.329546 3.179764
-3.284816 3.273099
3.091414 -3.815232
-3.762093 -2.432191
3.542056 2.778832
-1.736822 4.241041
2.127073 -2.983680
-4.323818 -3.938116
3.792121 5.135768
-4.786473 3.358547
2.624081 -3.260715
-4.009299 -2.978115
2.493525 1.963710
-2.513661 2.642162
1.864375 -3.176309
-3.171184 -3.572452
2.894220 2.489128
-2.562539 2.884438
3.491078 -3.947487
-2.565729 -2.012114
3.332948 3.983102
-1.616805 3.573188
2.280615 -2.559444
-2.651229 -3.103198
2.321395 3.154987
-1.685703 2.939697
3.031012 -3.620252
-4.599622 -2.185829
4.196223 1.126677
-2.133863 3.093686
4.668892 -2.562705
-2.793241 -2.149706
2.884105 3.043438
-2.967647 2.848696
4.479332 -1.764772
-4.905566 -2.911070
四、结果分析(占30%)
图片解释:算法输出了四个簇,此次聚类结果比较不错,四个较大的点为质心,
图片解释:此次聚类算法明显不如人意,这也是此算法的缺点,易达到局部最优。
图片解释;我们希望的算法聚类个数是4,这里只有3个聚类,出现这也的原因是:簇的四个初始质心是随机选取的,有可能随机到同一个数,如(22,13,33,22),这样导致有一个簇始终为空,不过出现这种情况的概率还是很小的,这也是此算法最糟糕的结果。
五、总结(占15%)
我们知道,任何算法都有它自身的优点与缺点,通过很多次的实验结果验证,我更加深刻理解了k-means的优点与缺点。
优点:k-means算法是聚类问题的经典算法,该算法简单快速。对于大数据量的数据,有相对较高的算法效率,它的伸缩性很高,常常以局部最优来结束算法。当簇是密集的,圆形的,团状的,而且簇与簇之前的区别明显时,它的聚类效果较好。
缺点:要求用户必须事先给出要生成的簇的数目k,这就好比世上先有鸡还是先有蛋的问题,这也是此算法无法避免的缺点。次算法对于初始值很敏感,对于不同的初始值,聚类的结果往往不同。对于噪声数据和孤立点数据非常敏感,少量的该数据能够对平均值产生巨大的影响。
这个算法也即将应用到一个平台上,对于这个算法的透侧研究,也将有助于我开发此软件,我知道,通过此次算法的研究,我已经学到了很多有用的东西。