single-pass聚类算法实现天气聚类


聚类算法介绍

(1)系统聚类法

系统聚类法的基本思想是:距离近的样品先聚成类,距离远的后聚成类。

根据类间定义的不同,系统聚类法又可以分成最短距离法、最长距离法、中间距离法、重心法、类平均法、可变类平均法、可变法、离差平方和法8种。

需要注意的是:

(1)该方法不需要事先指定聚类个数,而是根据最终的分类过程确定。

(2)为了直观的反映,可以把分类系统画成一张谱系图,所以系统聚类也称谱系分析。

(2)K-means聚类法

K-means聚类法是非常经典的聚类算法,有关资料很丰富,故本文不再赘述。

需要注意的是:

(1)该方法通过计算欧氏距离,比较样品间的相似度进行聚类。不过我也有看过,通过计算相关系数来聚类的。

(2)该法需要指定聚类个数,而K的确定是个难点,有很多针对K优化的方法。

(3)single-pass聚类法

含义适用

Single-pass clustering,中文名一般译作“单遍聚类”,它是一种简洁且高效的文本聚类算法。相比于常用的K-means聚类法,它的计算速度非常快,且不需要指定聚类个数,而是通过设定相似度阈值来限定。

Single-pass聚类算法同时是一种增量聚类算法(Incremental Clustering Algorithm),每个文档只需要流过算法一次,常用与文本主题的聚类中。它可以很好的应用于话题监测与追踪、在线事件监测等社交媒体大数据领域,特别适合流式数据(Streaming Data),比如微博的帖子信息等。

流数据:一组顺序、大量、快速、连续到达的数据序列。一般情况下,流数据可被视为一个随时间延续而无限增长的动态数据集合。
来源:百度百科

处理步骤

Single-pass算法顺序处理文本,以第一篇文档为种子,建立一个新主题。之后再进行新进入文档与已有主题的相似度,将该文档加入到与它相似度最大的、且大于一定阈值的主题中。如果与所有已有话题相似度都小于阈值,则以该文档为聚类种子,建立新的主题类别。其算法流程如下:

(1)以第一篇文档为种子,建立一个主题;

(2)将文档X向量化;

(3)将文档X与已有的所有话题均做相似度计算,可采用欧氏距离余弦距离等距离度量方法

(4)找出与文档X具有最大相似度的已有主题;

(5)若相似度值大于阈值θ ,则把文档X加入到有最大相似度的主题中,跳转至 (7);

(6)若相似度值小于阈值θ , 则文档X不属于任一已有主题, 需创建新的主题类别,同时将当前文本归属到新创建的主题类别中;

(7)聚类结束,等待下一篇文档进入

注:上述中讲相似度最大的划为一类,在实际代码中,所谓相似度最大大于某个阈值,也可以理解为距离最近(小)小于某个阈值。

样本描述

样本信息截图
将样本信息保存至TXT文件中,截图后如上所示。

有多少行我没数,用python中describe()一下就知道了,列数是十列。

日期后面两类是最高温和最低温,最后两列表示该地的经度和纬度。

代码实现

定义类和函数

首先定义了一个簇单元 ClusterUnit ,定义了一个单类 OnePassCluster ,定义了向量a与b间的欧式距离euclidian_distance。

# coding=utf-8
import numpy as np
from math import sqrt
import time
import 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.centroid)


# 计算向量a与向量b的欧式距离
def euclidian_distance(vec_a, vec_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[index],
                                              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("***********  single-pass的聚类结果展示  ***********")
        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( "所有节点的个数为: %s" % len(self.vectors))
        print("簇类的个数为:%s" % self.cluster_num)
        print("花费的时间为: %.9fs" % (self.spend_time / 1000))

运行之后,一共聚类十类,聚类个数从0-9。

如下图为第八个类别所包含的城市以及他们对应的索引:
第八个类别中内容
整体运行结果如下:
运行结果

调用与画图

之后通过实例化类和调用函数,来实现聚类

# 读取测试集
temperature_all_city = np.loadtxt('data1.txt', delimiter=",", usecols=(3, 4),encoding='utf-8')  # 读取聚类特征
xy = np.loadtxt('data1.txt', delimiter=",", usecols=(8, 9),encoding='utf-8')  # 读取各地经纬度
f = open('data1.txt', 'r',encoding='utf-8')
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)
print(temperature_all_city)


# 将聚类结果导出图
fig, ax = pl.subplots()
fig = zone_dict
c_map = pl.get_cmap('jet', clustering.cluster_num)
c = 0

for 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)
        ax.scatter(xy[node][0], xy[node][1])
    c += 1

#pl.axis('off') # 不显示坐标轴
pl.savefig('./map.jpg')
pl.show()

根据样本中经纬度的信息,并结合聚类算法的结果,可画图如下:
可视化画图

总结

本文主要给出single-pass聚类算法的实例,该例很好复现。希望对各位兄弟姐妹们有所帮助。

参考:https://blog.csdn.net/maqian5/article/details/107333316

本文数据与代码均引用他处,如未标注来源,请联系我更改加上。

需要本文数据和代码,可在本文评论区留言。希望对各位兄弟姐妹们有所帮助。

评论 33
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值