python 实现层次聚类
一. scipy实现
1. 函数说明
主要是两个函数:linkage,fcluster
1). linkage:
def linkage(y, method=‘single’, metric=‘euclidean’, optimal_ordering=False):
函数功能解释:执行层次/聚集聚类。
1). 参数(Parameters)
y:输入y可以是 1 维凝聚距离矩阵(距离向量)或 2 维观测矢量数组(坐标点的矩阵)。
method:method是指计算类间距离的方法,有如下几种方法:single,complete,average,weighted,centroid,ward
single:最近邻,把类与类间距离最近的作为类间距
从簇 u 和 簇 v 中找到一对距离最近的点的距离作为簇 u 和簇 v 的距离:
complete:最远邻,把类与类间距离最远的作为类间距
从簇 u 和 簇 v 中找到一对距离最远的点的距离作为簇 u 和簇 v 的距离:
average:平均距离,类与类间所有pairs距离的平均
如簇 u 中有若干个点 i,簇 v 中有若干个 j 点,簇 u 和簇 v的距离为:
weighted:在压缩距离矩阵上执行加权/ WPGMA链接
如簇u由簇 s 和簇 t 组成,那么簇 u 到 簇 v 的距离为:
centroid:质心距离,把类与类中的质心间距离作为类间距
ward:将要合并的群集的方差最小化。
2). 返回值(Returns)
Z(ndarray):层次聚类编码为一个linkage矩阵。
Z共有四列组成,第 1 字段与第 2 字段分别为聚类簇的编号,在初始距离前每个初始值被从0~n-1进行标识,每生成一个新的聚类簇就在此基础上增加一对新的聚类簇进行标识,第 3 个字段表示前两个聚类簇之间的距离,第 4 个字段表示新生成聚类簇所包含的元素的个数。
详细参考:https://docs.scipy.org/doc/scipy/reference/generated/scipy.cluster.hierarchy.linkage.html#scipy.cluster.hierarchy.linkage
2). fcluster:
def fcluster(Z, t, criterion=‘inconsistent’, depth=2, R=None, monocrit=None):
函数功能解释:从给定链接矩阵定义的层次聚类中形成平面聚类。
1.) 参数(Parameters)
Z:Z是linkage得到的矩阵,记录了层次聚类的层次信息;
t:是一个聚类的阈值
criterionstr:形成扁平簇的准则
inconsistent:
distance:以簇之间的距离作为划分准则
2). 返回值(Returns)
fcluster(ndarray):长度为n的数组。 T [i]是原始观测值 i 所属的平面簇数。
2. 示例含完整算法:
如对以下 5 个点进行凝聚层次聚类。
x
y
点0
1
2
点1
2
3
点2
-3
3
点3
-2
-1
点4
5
-1
在坐标轴上的位置:
层次聚类结果的聚类树为:
完整算法如下:
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : pentiumCM
@Email : [email protected]
@Software: PyCharm
@File : sci_cluster.py
@Time : 2020/4/15 22:21
@desc : scipy实现层次聚类
'''
import numpy as np
from scipy.cluster.hierarchy import dendrogram, linkage, fcluster
from matplotlib import pyplot as plt
data = np.array([[1, 2], [2, 3], [-3, 3], [-2, -1], [5, -1]])
# 画点
plt.scatter(x=data[:, 0:1], y=data[:, 1:2], marker='.', color='red')
n = np.arange(data.shape[0])
for i, txt in enumerate(n):
plt.annotate(txt, (data[i:i + 1, 0:1], data[i:i + 1, 1:2]))
plt.show()
# 1. 层次聚类
# linkage方法用于计算两个聚类簇s和t之间的距离d(s,t)
# 层次聚类编码为一个linkage矩阵。
Z = linkage(data, 'average')
print("聚类过程:", Z)
# 从给定链接矩阵定义的层次聚类中形成平面聚类。
# distance:以距离为划分距离的准则
f = fcluster(Z, 4, 'distance')
print("平面聚类结果:", f)
fig = plt.figure(figsize=(5, 3))
# 将层级聚类结果以树状图表示出来
dn = dendrogram(Z)
plt.show()
算法运行结果:
聚类过程: [[0. 1. 1.41421356 2. ]
[2. 3. 4.12310563 2. ]
[5. 6. 4.75565014 4. ]
[4. 7. 6.48606798 5. ]]
平面聚类结果: [1 1 2 3 4]
聚类的主要信息在 Z = linkage(data, ‘average’)中。
Z共有四列组成,第 1 字段与第 2 字段分别为聚类簇的编号,在初始距离前每个初始值被从0~n-1进行标识,每生成一个新的聚类簇就在此基础上增加一对新的聚类簇进行标识,第 3 个字段表示前两个聚类簇之间的距离,第 4 个字段表示新生成聚类簇所包含的元素的个数。
层次聚类可以一次性聚类出所有的情况,当生成出聚类树的结果时,可以通过在聚类树上画水平线(例如在上面算法中,水平线是以簇之间距离为准则)来选择聚成几类的结果。如用户需要聚成两类:
只需要从上往下画出如上图的水平线来判断聚成两类的结果,可以看出两类的结果,一类为 4,另一类为0、1、2、3。
如果需要聚成3类,只需要将水平线往下平移即可知道聚成三类的结果。
二、sklearn实现
1. 函数说明:
sklearn库下的层次聚类是在sklearn.cluster的 AgglomerativeClustering中:
def __init__(self, n_clusters=2, affinity="euclidean",
memory=None,
connectivity=None, compute_full_tree='auto',
linkage='ward', distance_threshold=None):
AgglomerativeClustering 类的构造函数的参数有 簇的个数n_clusters,连接方法linkage,连接度量选项affinity 三个重要参数:
n_clusters:用户指定需要聚成几类。
linkage:选择计算簇与簇之间距离的策略,包含:ward,complete,average,single
ward:将要合并的群集的方差最小化。
complete:完全距离/最大距离,使用两组中所有观察值之间的最大距离。
single:最小距离,使用两组中所有观测值之间的最小距离。
average:平均距离,使用两组的每个观测值的距离平均值。
affinity:是一个簇间距离的计算方法。 可以是 “ euclidean(欧式距离)”,“ l1”,“ l2”,“manhattan(曼哈顿距离)”,“cosine(余弦)” 或 “precomputed(预先计算)”。 如果链接为“ward”,则仅接受“欧式距离”。 如果 “precomputed(预先计算)”,则需要距离矩阵作为拟合方法的输入。
距离矩阵 的生成方法:假设用户有 n 个观测点,那么先依次构造这 n 个点两两间的距离列表,即长度为 n*(n-1)/2 的距离列表,然后通过 scipy.spatial.distance 的 dist 库的 squareform 函数就可以构造距离矩阵了。这种方式的好处是用户可以使用自己定义的方法计算任意两个观测点的距离,然后再进行聚类。
后面也会给出基于自定义的距离矩阵进行的聚类算法。
详细参考源码文档:https://scikit-learn.org/stable/modules/generated/sklearn.cluster.AgglomerativeClustering.html#sklearn.cluster.AgglomerativeClustering
2. 完整算法:
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : pentiumCM
@Email : [email protected]
@Software: PyCharm
@File : sklearn_hierarchical_cluster.py
@Time : 2020/4/23 15:00
@desc : sklearn的层次聚类
'''
import numpy as np
from matplotlib import pyplot as plt
from sklearn.cluster import AgglomerativeClustering
data = np.array([[1, 2], [2, 3], [-3, 3], [-2, -1], [5, -1]])
# 画点
plt.scatter(x=data[:, 0:1], y=data[:, 1:2], marker='.', color='red')
n = np.arange(data.shape[0])
for i, txt in enumerate(n):
plt.annotate(txt, (data[i:i + 1, 0:1], data[i:i + 1, 1:2]))
plt.show()
# 训练模型
ac = AgglomerativeClustering(n_clusters=3, affinity='euclidean', linkage='average')
clustering = ac.fit(data)
print("每个数据所属的簇编号", clustering.labels_)
print("每个簇的成员", clustering.children_)
算法运行结果:
每个数据所属的簇编号 [2 2 0 0 1]
每个簇的成员 [[0 1]
[2 3]
[5 6]
[4 7]]
借助 scipy 生成的聚类树,我们来理解一下聚类的结果:
clustering.labels_:表示每个数据所属于哪一个簇。
[2 2 0 0 1]:表示数据0、1分为一簇,2、3分为一簇,4分为一簇。
clustering.children_:表示每个簇中有哪些元素。
[[0 1] [2 3] [5 6] [4 7]]:首先将数据初始化为簇 0 ~ n-1,然后簇0、1合并为簇5,簇2、3合并为簇6,簇5、6合并为簇7,最后簇4、7合并。
补充基于预计算的距离矩阵的算法:
完整算法如下:
#!/usr/bin/env python
# encoding: utf-8
'''
@Author : pentiumCM
@Email : [email protected]
@Software: PyCharm
@File : sklearn_hierarchical_cluster.py
@Time : 2020/4/23 15:00
@desc : sklearn的层次聚类
'''
import numpy as np
from matplotlib import pyplot as plt
from sklearn.cluster import AgglomerativeClustering
from sklearn.metrics.pairwise import euclidean_distances
import scipy.spatial.distance as dist
from scipy.cluster.hierarchy import dendrogram, linkage
data = np.array([[1, 2], [2, 3], [-3, 3], [-2, -1], [5, -1]])
# 画点
plt.scatter(x=data[:, 0:1], y=data[:, 1:2], marker='.', color='red')
n = np.arange(data.shape[0])
for i, txt in enumerate(n):
plt.annotate(txt, (data[i:i + 1, 0:1], data[i:i + 1, 1:2]))
plt.show()
# 聚类方式一
# 训练模型
ac = AgglomerativeClustering(n_clusters=3, affinity='euclidean', linkage='average')
clustering = ac.fit(data)
print("每个数据所属的簇编号:", clustering.labels_)
print("每个簇的成员:", clustering.children_)
# 聚类的方式二
# 自定义距离矩阵
num = data.shape[0]
dist_matrix = np.mat(np.zeros((num, num)))
for i in range(num):
for j in range(i, num):
distence = euclidean_distances(data[i:i + 1], data[j:j + 1])
dist_matrix[i:i + 1, j:j + 1] = distence
dist_matrix[j:j + 1, i:i + 1] = dist_matrix[i:i + 1, j:j + 1]
# 基于自定义的聚类矩阵进行聚类
model = AgglomerativeClustering(n_clusters=3, affinity='precomputed', linkage='average')
clustering2 = model.fit(dist_matrix)
print("自定义距离矩阵聚类方式:")
print("每个数据所属的簇编号:", clustering2.labels_)
print("每个簇的成员:", clustering2.children_)
# 调整距离矩阵的形状
dist_matrix = dist.squareform(dist_matrix)
# linkage方法用于计算两个聚类簇s和t之间的距离d(s,t)
# 层次聚类编码为一个linkage矩阵。
Z = linkage(dist_matrix, 'average')
print("聚类过程:", Z)
# 将层级聚类结果以树状图表示出来
fig = plt.figure(figsize=(5, 3))
dn = dendrogram(Z)
plt.show()
算法运行结果:
每个数据所属的簇编号: [2 2 0 0 1]
每个簇的成员: [[0 1]
[2 3]
[5 6]
[4 7]]
自定义距离矩阵聚类方式:
每个数据所属的簇编号: [2 2 0 0 1]
每个簇的成员: [[0 1]
[2 3]
[5 6]
[4 7]]
聚类过程: [[0. 1. 1.41421356 2. ]
[2. 3. 4.12310563 2. ]
[5. 6. 4.75565014 4. ]
[4. 7. 6.48606798 5. ]]
上面的算法包含了sklearn的两种聚类方式,方式二为预计算的方式:算法预先计算了 期初各个数据点之间的距离矩阵dist_matrix,然后在聚类时,affinity='precomputed'。
对比之下,对于层次聚类的方便理解的方式,大家可以采用scipy的方式。
参考资料:
原文链接:https://blog.csdn.net/pentiumCM/article/details/105695414