这篇博客开始另外一种聚类——层次聚类,层次聚类和K-Means是同一类的,属于划分聚类。
概述
层次聚类方法对给定的数据集进行层次的分解,直到满足某种条件为止,传统的层次聚类算法主要分为两大类算法:
- 凝聚的层次聚类:AGNES算法( AGglomerative NESting )=>采用自底向上的策略。 最初将每个对象作为一个簇,然后这些簇根据某些准则被一步一步合并,两个簇间的距离可以由这两个不同簇中距离最近的数据点的相似度来确定;聚类的合并过程反复进行直到所有的对象满足簇数目。
- 分裂的层次聚类:DIANA算法(DIvisive ANALysis)=>采用自顶向下的策略(与二分k-means非常类似)。首先将所有对象置于一个簇中,然后按照某种既定的规则(聚类规则,如k-means)逐渐细分为越来越小的簇(比如最大的欧式距离),直到达到某个终结条件(簇数目或者簇距离达到阈值)。
AGNES和DIANA算法优缺点
- 简单,理解容易
- 合并点/分裂点选择不太容易
- 合并/分类的操作不能进行撤销
- 大数据集不太适合
- 执行效率较低 O(t∗n2),t为迭代次数,n为样本点数
接下来着重看一下AGNES算法中簇间距离的合并策略
- 最小距离(SL聚类)
两个聚簇中最近的两个样本之间的距离(single/word-linkage聚类法)
最终得到模型容易形成链式结构 - 最大距离(CL聚类)
两个聚簇中最远的两个样本的距离(complete-linkage聚类法)
如果存在异常值,那么构建可能不太稳定 - 平均距离(AL聚类)
两个聚簇中样本间两两距离的平均值(average-linkage聚类法)
两个聚簇中样本间两两距离的中值(median-linkage聚类法)
不同的合并策略
代码
当然这里也只是以AGNES为例。
API
参数
- n_clusters:聚类数
- affinity : string or callable, default: “euclidean”,距离度量公式,默认为欧式
- memory:内存或磁盘文件,因为计算量比较大,比较耗内存
- linkage : {“ward”, “complete”, “average”, “single”}, optional (default=”ward”),相似度度量方式
代码
import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
from sklearn.cluster import AgglomerativeClustering
from sklearn.neighbors import kneighbors_graph ## KNN的K近邻计算
import sklearn.datasets as ds
import warnings
## 设置属性防止中文乱码及拦截异常信息
mpl.rcParams['font.sans-serif'] = [u'SimHei']
mpl.rcParams['axes.unicode_minus'] = False
warnings.filterwarnings(action='ignore', category=UserWarning)
## 模拟数据产生: 产生600条数据
np.random.seed(0)
n_clusters = 4
N = 1000
data1, y1 = ds.make_blobs(n_samples=N, n_features=2, centers=((-1, 1), (1, 1), (1, -1), (-1, -1)), random_state=0)
n_noise = int(0.1*N)
r = np.random.rand(n_noise, 2)
min1, min2 = np.min(data1, axis=0)
max1, max2 = np.max(data1, axis=0)
r[:, 0] = r[:, 0] * (max1-min1) + min1
r[:, 1] = r[:, 1] * (max2-min2) + min2
data1_noise = np.concatenate((data1, r), axis=0)
y1_noise = np.concatenate((y1, [4]*n_noise))
#拟合月牙形数据
data2, y2 = ds.make_moons(n_samples=N, noise=.05)
data2 = np.array(data2)
n_noise = int(0.1 * N)
r = np.random.rand(n_noise, 2)
min1, min2 = np.min(data2, axis=0)
max1, max2 = np.max(data2, axis=0)
r[:, 0] = r[:, 0] * (max1 - min1) + min1
r[:, 1] = r[:, 1] * (max2 - min2) + min2
data2_noise = np.concatenate((data2, r), axis=0)
y2_noise = np.concatenate((y2, [3] * n_noise))
def expandBorder(a, b):
d = (b - a) * 0.1
return a-d, b+d
## 画图
# 给定画图的颜色
cm = mpl.colors.ListedColormap(['#FF0000', '#00FF00', '#0000FF', '#d8e507', '#F0F0F0'])
plt.figure(figsize=(14, 12), facecolor='w')
linkages = ("ward", "complete", "average")#把几种距离方法,放到list里,后面直接循环取值
for index, (n_clusters, data, y) in enumerate(((4, data1, y1), (4, data1_noise, y1_noise),
(2, data2, y2), (2, data2_noise, y2_noise))):
# 前面的两个4表示几行几列,第三个参数表示第几个子图(从1开始,从左往右数)
plt.subplot(4, 4, 4*index+1)
plt.scatter(data[:, 0], data[:, 1], c=y, cmap=cm)
plt.title(u'原始数据', fontsize=17)
plt.grid(b=True, ls=':')
min1, min2 = np.min(data, axis=0)
max1, max2 = np.max(data, axis=0)
plt.xlim(expandBorder(min1, max1))
plt.ylim(expandBorder(min2, max2))
# 计算类别与类别的距离(只计算最接近的七个样本的距离) -- 希望在agens算法中,在计算过程中不需要重复性的计算点与点之间的距离
connectivity = kneighbors_graph(data, n_neighbors=7, mode='distance', metric='minkowski', p=2, include_self=True)
connectivity = (connectivity + connectivity.T)
for i, linkage in enumerate(linkages):
##进行建模,并传值
print(n_clusters)
ac = AgglomerativeClustering(n_clusters=n_clusters, affinity='euclidean',
connectivity=connectivity, linkage=linkage)
ac.fit(data)
y = ac.labels_
plt.subplot(4, 4, i+2+4*index)
plt.scatter(data[:, 0], data[:, 1], c=y, cmap=cm)
plt.title(linkage, fontsize=17)
plt.grid(b=True, ls=':')
plt.xlim(expandBorder(min1, max1))
plt.ylim(expandBorder(min2, max2))
plt.suptitle(u'AGNES层次聚类的不同合并策略', fontsize=30)
plt.tight_layout(0.5, rect=(0, 0, 1, 0.95))
plt.show()
4
4
4
4
4
4
2
2
2
2
2
2
最后看一下效果图: