文本聚类算法之一趟聚类(One-pass Cluster)算法的python实现

来源:互联网 发布:mes软件系统架构 编辑:程序博客网 时间:2024/06/05 10:09

一、算法简介

一趟聚类算法是由蒋盛益教授提出的无监督聚类算法,该算法具有高效、简单的特点。数据集只需要遍历一遍即可完成聚类。算法对超球状分布的数据有良好的识别,对凸型数据分布识别较差。一趟聚类可以在大规模数据,或者二次聚类中,或者聚类与其他算法结合的情况下,发挥其高效、简单的特点;

 

算法流程:

1. 初始时从数据集读入一个新的对象

2. 以这个对象构建一个新的簇

3. 若达到数据集末尾,则转6,否则读入一个新的对象;计算它与每个已有簇之间的距离,并选择与它距离最小的簇。

4. 若最小距离超过给定的阈值r,转2

5. 否则将对象并入该簇,并更新簇心,转3

6. 结束

 

在本算法中,采用的是欧式距离公式计算节点与簇心之间的距离

欧式距离公式:



二、实验

实验数据集:

中国天气的数据集,包括中国各个城市每年的最低和最高温以及该城市的x,y坐标。

实验数据:

https://raw.githubusercontent.com/Hareric/ClusterTool/master/Other/c2.txt


python代码:

# coding=utf-8import numpy as npfrom math import sqrtimport timeimport matplotlib.pylab as pl# 定义一个簇单元class ClusterUnit:    def __init__(self):        self.node_list = []  # 该簇存在的节点列表        self.node_num = 0  # 该簇节点数        self.centroid = None  # 该簇质心    def add_node(self, node, node_vec):        """        为本簇添加指定节点,并更新簇心         node_vec:该节点的特征向量         node:节点         return:null        """        self.node_list.append(node)        try:            self.centroid = (self.node_num * self.centroid + node_vec) / (self.node_num + 1)  # 更新簇心        except TypeError:            self.centroid = np.array(node_vec) * 1  # 初始化质心        self.node_num += 1  # 节点数加1    def remove_node(self, node):        # 移除本簇指定节点        try:            self.node_list.remove(node)            self.node_num -= 1        except ValueError:            raise ValueError("%s not in this cluster" % node)  # 该簇本身就不存在该节点,移除失败    def move_node(self, node, another_cluster):        # 将本簇中的其中一个节点移至另一个簇        self.remove_node(node=node)        another_cluster.add_node(node=node)# cluster_unit = ClusterUnit()# cluster_unit.add_node(1, [1, 1, 2])# cluster_unit.add_node(5, [2, 1, 2])# cluster_unit.add_node(3, [3, 1, 2])# print cluster_unit.centroiddef euclidian_distance(vec_a, vec_b):    # 计算向量a与向量b的欧式距离    diff = vec_a - vec_b    return sqrt(np.dot(diff, diff))  # dot计算矩阵内积class OnePassCluster:    def __init__(self, t, vector_list):        # t:一趟聚类的阈值        self.threshold = t  # 一趟聚类的阈值        self.vectors = np.array(vector_list)        self.cluster_list = []  # 聚类后簇的列表        t1 = time.time()        self.clustering()        t2 = time.time()        self.cluster_num = len(self.cluster_list)  # 聚类完成后 簇的个数        self.spend_time = t2 - t1  # 聚类花费的时间    def clustering(self):        self.cluster_list.append(ClusterUnit())  # 初始新建一个簇        self.cluster_list[0].add_node(0, self.vectors[0])  # 将读入的第一个节点归于该簇        for index in range(len(self.vectors))[1:]:            min_distance = euclidian_distance(vec_a=self.vectors[0],                                              vec_b=self.cluster_list[0].centroid)  # 与簇的质心的最小距离            min_cluster_index = 0  # 最小距离的簇的索引            for cluster_index, cluster in enumerate(self.cluster_list[1:]):                # enumerate会将数组或列表组成一个索引序列                # 寻找距离最小的簇,记录下距离和对应的簇的索引                distance = euclidian_distance(vec_a=self.vectors[index],                                              vec_b=cluster.centroid)                if distance < min_distance:                    min_distance = distance                    min_cluster_index = cluster_index + 1            if min_distance < self.threshold:  # 最小距离小于阈值,则归于该簇                self.cluster_list[min_cluster_index].add_node(index, self.vectors[index])            else:  # 否则新建一个簇                new_cluster = ClusterUnit()                new_cluster.add_node(index, self.vectors[index])                self.cluster_list.append(new_cluster)                del new_cluster    def print_result(self, label_dict=None):        # 打印出聚类结果        # label_dict:节点对应的标签字典        print "*******  one-pass cluster result  ***********"        for index, cluster in enumerate(self.cluster_list):            print "cluster:%s" % index  # 簇的序号            print cluster.node_list   # 该簇的节点列表            if label_dict is not None:                print " ".join([label_dict[n] for n in cluster.node_list])  # 若有提供标签字典,则输出该簇的标签            print "node num: %s" % cluster.node_num            print "-------------"        print "the number of nodes %s" % len(self.vectors)        print "the number of cluster %s" % self.cluster_num        print "spend time %.9fs" % (self.spend_time / 1000)# 读取测试集temperature_all_city = np.loadtxt('c2.txt', delimiter=",", usecols=(3, 4))  # 读取聚类特征xy = np.loadtxt('c2.txt', delimiter=",", usecols=(8, 9))  # 读取各地经纬度f = open('c2.txt', 'r')lines = f.readlines()zone_dict = [i.split(',')[1] for i in lines]  # 读取地区并转化为字典f.close()# 构建一趟聚类器clustering = OnePassCluster(vector_list=temperature_all_city, t=9)clustering.print_result(label_dict=zone_dict)# 将聚类结果导出图fig, ax = pl.subplots()fig = zone_dictc_map = pl.get_cmap('jet', clustering.cluster_num)c = 0for cluster in clustering.cluster_list:    for node in cluster.node_list:        ax.scatter(xy[node][0], xy[node][1], c=c, s=30, cmap=c_map, vmin=0, vmax=clustering.cluster_num)    c += 1pl.show()


实验结果:


1 0
原创粉丝点击