实验目的
了解基本聚类算法的基本原理,并掌握Python语言中实现Kmeans聚类算法的函数方法
实验原理
聚类就是将相似的事物聚集在一起,而将不相似的事物划分到不同的类别的过程,是数据分析之中十分重要的一种手段。在数据分析的术语之中,聚类和分类是两种技术。分类是指我们已经知道了事物的类别,需要从样品中学习分类的规则,是一种监督式学习;而聚类则是由我们来给定简单的规则,从而得到类别,是一种无监督学习。
在K均值算法中,质心是定义聚类原型(也就是机器学习获得的结果)的核心。在介绍算法实施的具体过程中,我们将演示质心的计算方法。而且你将看到除了第一次的质心是被指定的以外,此后的质心都是经由计算均值而获得的。
首先,选择K个初始质心(这K个质心并不要求来自于样本数据集),其中K是用户指定的参数,也就是所期望的簇的个数。每个数据点都被收归到距其最近之质心的分类中,而同一个质心所收归的点集为一个簇。然后,根据本次分类的结果,更新每个簇的质心。重复上述数据点分类与质心变更步骤,直到簇内数据点不再改变,或者等价地说,直到质心不再改变。
KMeans算法的基本思想是初始随机给定K个簇中心,按照最邻近原则把待分类样本点分到各个簇。然后按平均法重新计算各个簇的质心,从而确定新的簇心。一直迭代,直到簇心的移动距离小于某个给定的值。
复杂度分析
- 时间复杂度:O(tKmn),其中,t为迭代次数,K为簇的数目,m为记录数,n为维数
- 空间复杂度:O((m+K)n),其中,K为簇的数目,m为记录数,n为维数
实验步骤
K-Means聚类算法主要分为三个步骤:
1,初始化k个聚类中心。
2,计算出每个对象跟这k个中心的距离(相似度计算,这个下面会提到),假如x这个对象跟y这个中心的距离最小(相似度最大),那么x属于y这个中心。这一步就可以得到初步的k个聚类 。
3,在第二步得到的每个聚类分别计算出新的聚类中心,和旧的中心比对,假如不相同,则继续第2步,直到新旧两个中心相同,说明聚类不可变,已经成功 。
本实验采用数据集为scikit -learn所包含的数据集,点击链接查看数据集描述。
machine-learning-databases/iris点击打开链接
可以通过以下命令查看数据。
- from sklearn import datasets
- iris=datasets.load_iris()
- #data对应了样本的4个特征,150行4列
- print iris.data.shape
- #显示样本特征的前5行
- print iris.data[:5]
- #target对应了样本的类别(目标属性),150行1列
- print iris.target.shape
- #显示所有样本的目标属性
- print iris.target
结果为:
第一步,将文件中的数据读入到dataset列表中,通过len(dataset[0])来获取数据维数,在测试样例中是四维。
第二步,产生聚类的初始位置。首先扫描数据,获取每一维数据分量中的最大值和最小值,然后在这个区间上随机产生一个值,循环k次(k为所分的类别),这样就产生了聚类初始中心(k个)
第三步,按照最短距离(欧式距离)原则将所有样本分配到k个聚类中心中的某一个,这步操作的结果是产生列表assigments,可以通过Python中的zip函数整合成字典。注意到原始聚类中心可能不在样本中,因此可能出现分配的结果出现某一个聚类中心点集合为空,此时需要结束,提示“随机数产生错误,需要重新运行”,以产生合适的初始中心。
第四步,计算各个聚类中心的新向量,更新距离,即每一类中每一维均值向量。然后再进行分配,比较前后两个聚类中心向量是否相等,若不相等则进行循环,否则终止循环,进入下一步。
最后,将结果输出到文件和屏幕中
结果为:
完整代码如下:
- # coding=gbk
- #python edition: Python3.4.1,2014,9,24
- from collections import defaultdict
- from random import uniform
- from math import sqrt
- from sklearn.datasets import load_iris
- def read_points():
- dataset=[]
- with open('Iris.txt','r') as file:
- for line in file:
- if line =='\n':
- continue
- dataset.append(list(map(float,line.split(' '))))
- file.close()
- return dataset
- def write_results(listResult,dataset,k):
- with open('result.txt','a') as file:
- for kind in range(k):
- file.write( "CLASSINFO:%d\n"%(kind+1) )
- for j in listResult[kind]:
- file.write('%d\n'%j)
- file.write('\n')
- file.write('\n\n')
- file.close()
- def point_avg(points):
- dimensions=len(points[0])
- new_center=[]
- for dimension in range(dimensions):
- sum=0
- for p in points:
- sum+=p[dimension]
- new_center.append(float("%.8f"%(sum/float(len(points)))))
- return new_center
- def update_centers(data_set ,assignments,k):
- new_means = defaultdict(list)
- centers = []
- for assignment ,point in zip(assignments , data_set):
- new_means[assignment].append(point)
- for i in range(k):
- points=new_means[i]
- centers.append(point_avg(points))
- return centers
- def assign_points(data_points,centers):
- assignments=[]
- for point in data_points:
- shortest=float('inf')
- shortest_index = 0
- for i in range(len(centers)):
- value=distance(point,centers[i])
- if value<shortest:
- shortest=value
- shortest_index=i
- assignments.append(shortest_index)
- if len(set(assignments))<len(centers) :
- print("\n--!!!产生随机数错误,请重新运行程序!!!!--\n")
- exit()
- return assignments
- def distance(a,b):
- dimention=len(a)
- sum=0
- for i in range(dimention):
- sq=(a[i]-b[i])**2
- sum+=sq
- return sqrt(sum)
- def generate_k(data_set,k):
- centers=[]
- dimentions=len(data_set[0])
- min_max=defaultdict(int)
- for point in data_set:
- for i in range(dimentions):
- value=point[i]
- min_key='min_%d'%i
- max_key='max_%d'%i
- if min_key not in min_max or value<min_max[min_key]:
- min_max[min_key]=value
- if max_key not in min_max or value>min_max[max_key]:
- min_max[max_key]=value
- for j in range(k):
- rand_point=[]
- for i in range(dimentions):
- min_val=min_max['min_%d'%i]
- max_val=min_max['max_%d'%i]
- tmp=float("%.8f"%(uniform(min_val,max_val)))
- rand_point.append(tmp)
- centers.append(rand_point)
- return centers
- def k_means(dataset,k):
- k_points=generate_k(dataset,k)
- assignments=assign_points(dataset,k_points)
- old_assignments=None
- while assignments !=old_assignments:
- new_centers=update_centers(dataset,assignments,k)
- old_assignments=assignments
- assignments=assign_points(dataset,new_centers)
- result=list(zip(assignments,dataset))
- print('\n\n---------------------------------分类结果---------------------------------------\n\n')
- for out in result :
- print(out,end='\n')
- print('\n\n---------------------------------标号简记---------------------------------------\n\n')
- listResult=[[] for i in range(k)]
- count=0
- for i in assignments:
- listResult[i].append(count)
- count=count+1
- write_results(listResult,dataset,k)
- for kind in range(k):
- print("第%d类数据有:"%(kind+1))
- count=0
- for j in listResult[kind]:
- print(j,end=' ')
- count=count+1
- if count%25==0:
- print('\n')
- print('\n')
- print('\n\n--------------------------------------------------------------------------------\n\n')
- def main():
- dataset=read_points()
- k_means(dataset,3)
- if __name__ == "__main__":
- main()