层次聚类是一种常用的数据聚类方法,它通过构建一个层次嵌套的聚类树来组织数据。在高维空间中,不同的度量方式对聚类结果的影响尤为显著。本文通过生成三组波形数据,来展示不同度量方式(欧几里得距离、曼哈顿距离和余弦距离)对层次聚类算法的影响。
在本例中,生成了两组波形数据(波形1和波形2),它们之间是成比例的关系。余弦距离对数据的缩放不敏感,因此无法区分这两种波形。即使在没有噪声的情况下,使用余弦距离进行聚类也无法将波形1和波形2分开。
为了模拟实际情况,向波形数据中添加了观测噪声。这些噪声非常稀疏,只有6%的时间点包含噪声。因此,这种噪声的l1范数(即“曼哈顿距离”)远小于其l2范数(即“欧几里得距离”)。这一点可以从类间距离矩阵的对角线值中看出,这些值描述了类的分布范围,对于欧几里得距离来说,这些值远大于曼哈顿距离。
当对数据应用聚类算法时,发现聚类结果反映了距离矩阵中的情况。对于欧几里得距离,由于噪声的影响,类别之间的分离度不高,因此聚类结果无法将波形分开。对于曼哈顿距离,分离度良好,波形类别得以恢复。最后,余弦距离根本无法将波形1和波形2分开,因此聚类将它们归为同一类。
以下是使用Python语言和scikit-learn库实现上述层次聚类算法的代码示例。
import matplotlib.pyplot as plt
import numpy as np
from sklearn.cluster import AgglomerativeClustering
from sklearn.metrics import pairwise_distances
# 设置随机种子以保证结果的可重复性
np.random.seed(0)
# 生成波形数据
n_features = 2000
t = np.pi * np.linspace(0, 1, n_features)
def sqr(x):
return np.sign(np.cos(x))
X = list()
y = list()
for i, (phi, a) in enumerate([(0.5, 0.15), (0.5, 0.6), (0.3, 0.2)]):
for _ in range(30):
phase_noise = 0.01 * np.random.normal()
amplitude_noise = 0.04 * np.random.normal()
additional_noise = 1 - 2 * np.random.rand(n_features)
# 使噪声稀疏
additional_noise[np.abs(additional_noise) < 0.997] = 0
X.append(12 * ((a + amplitude_noise) * (sqr(6 * (t + phi + phase_noise))) + additional_noise))
y.append(i)
X = np.array(X)
y = np.array(y)
n_clusters = 3
labels = ("波形1", "波形2", "波形3")
colors = ["#f7bd01", "#377eb8", "#f781bf"]
# 绘制真实标签
plt.figure()
plt.axes([0, 0, 1, 1])
for l, color, n in zip(range(n_clusters), colors, labels):
lines = plt.plot(X[y == l].T, c=color, alpha=0.5)
lines[0].set_label(n)
plt.legend(loc="best")
plt.axis("tight")
plt.axis("off")
plt.suptitle("真实标签", size=20, y=1)
# 绘制距离矩阵
for index, metric in enumerate(["cosine", "euclidean", "cityblock"]):
avg_dist = np.zeros((n_clusters, n_clusters))
plt.figure(figsize=(5, 4.5))
for i in range(n_clusters):
for j in range(n_clusters):
avg_dist[i, j] = pairwise_distances(X[y == i], X[y == j], metric=metric).mean()
avg_dist /= avg_dist.max()
for i in range(n_clusters):
for j in range(n_clusters):
t = plt.text(i, j, "%5.3f" % avg_dist[i, j], verticalalignment="center", horizontalalignment="center")
t.set_path_effects([PathEffects.withStroke(linewidth=5, foreground="w", alpha=0.5)])
plt.imshow(avg_dist, interpolation="nearest", cmap="cividis", vmin=0)
plt.xticks(range(n_clusters), labels, rotation=45)
plt.yticks(range(n_clusters), labels)
plt.colorbar()
plt.suptitle("类间%s距离" % metric, size=18, y=1)
plt.tight_layout()
# 绘制聚类结果
for index, metric in enumerate(["cosine", "euclidean", "cityblock"]):
model = AgglomerativeClustering(n_clusters=n_clusters, linkage="average", metric=metric)
model.fit(X)
plt.figure()
plt.axes([0, 0, 1, 1])
for l, color in zip(np.arange(model.n_clusters), colors):
plt.plot(X[model.labels_ == l].T, c=color, alpha=0.5)
plt.axis("tight")
plt.axis("off")
plt.suptitle("层次聚类(metric=%s)" % metric, size=20, y=1)
plt.show()
上述代码首先生成了三组波形数据,并添加了稀疏噪声。然后,使用不同的度量方式计算了类间距离矩阵,并绘制了这些矩阵。最后,应用了层次聚类算法,并展示了不同度量方式下的聚类结果。