系统聚类法的基本思想是:距离近的样品先聚成类,距离远的后聚成类。
根据类间定义的不同,系统聚类法又可以分成最短距离法、最长距离法、中间距离法、重心法、类平均法、可变类平均法、可变法、离差平方和法8种。
需要注意的是:
(1)该方法不需要事先指定聚类个数,而是根据最终的分类过程确定。
(2)为了直观的反映,可以把分类系统画成一张谱系图,所以系统聚类也称谱系分析。
K-means聚类法是非常经典的聚类算法,有关资料很丰富,故本文不再赘述。
需要注意的是:
(1)该方法通过计算欧氏距离,比较样品间的相似度进行聚类。不过我也有看过,通过计算相关系数来聚类的。
(2)该法需要指定聚类个数,而K的确定是个难点,有很多针对K优化的方法。
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)聚类结束,等待下一篇文档进入
注:上述中讲相似度最大的划为一类,在实际代码中,所谓相似度最大大于某个阈值,也可以理解为距离最近(小)小于某个阈值。
有多少行我没数,用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
本文数据与代码均引用他处,如未标注来源,请联系我更改加上。
需要本文数据和代码,可在本文评论区留言。希望对各位兄弟姐妹们有所帮助。