如何在基于Z-score矩阵的KMeans聚类中同时检测相关性与反相关性?
我明白你的需求——标准KMeans基于欧氏距离计算相似度,只会把数值完全匹配的行(比如你示例里的前两行)归为一类,但你希望它能识别出反相关的行(比如符号完全相反的向量),把这类行也划分到同一聚类中。下面我会给你几个实用的解决方案,附代码示例:
为什么标准KMeans做不到?
标准KMeans用欧氏距离衡量样本间的相似度,反相关向量的欧氏距离会非常大(比如你示例里的[0,-1,1,0]和[0,1,-1,0],欧氏距离是√[(0)^2 + (-2)^2 + (2)^2 + 0] = √8),远大于相同向量的距离(0),所以KMeans会把它们分到不同簇。
解决方案1:将向量转换为“规范形式”
最简单的思路是把反相关的向量转换成完全相同的形式,这样标准KMeans就能直接处理。比如我们可以为每个向量定义一个“规范版本”:让向量的第一个非零元素为正,如果原向量第一个非零元素是负的,就取它的相反数。
代码示例
import numpy as np from sklearn.cluster import KMeans # 你的示例数据 X = np.array([ [0, -1, 1, 0], [0, -1, 1, 0], [0, 1, -1, 0], [1, 1, -1, -1], [-1, -1, 1, 1] ]) # 定义向量规范化函数 def normalize_vector(row): # 找到第一个非零元素的位置 first_non_zero_idx = np.argmax(row != 0) # 如果第一个非零元素为负,取向量的相反数 if row[first_non_zero_idx] < 0: return -row return row # 转换所有向量为规范形式 X_normalized = np.array([normalize_vector(row) for row in X]) print("转换后的矩阵:\n", X_normalized) # 用标准KMeans聚类 kmeans = KMeans(n_clusters=2, random_state=42) cluster_labels = kmeans.fit_predict(X_normalized) print("\n聚类结果标签:", cluster_labels)
结果说明
转换后,原反相关的[0,-1,1,0]会变成[0,1,-1,0],和第三行完全一致;[-1,-1,1,1]会变成[1,1,-1,-1],和第四行一致。最终KMeans会把前3行归为一类,后2行归为一类,完全符合你的需求。
优缺点
- ✅ 简单快速,直接兼容标准KMeans
- ❌ 依赖第一个非零元素的符号,如果数据存在噪声导致第一个非零元素不稳定,可能影响结果
解决方案2:基于余弦相似度的聚类
余弦相似度衡量的是向量的方向相似性,相同方向的向量余弦相似度为1,反方向的为-1。我们可以基于余弦相似度的绝对值来定义距离(即忽略符号差异),再用支持自定义距离的聚类算法(比如层次聚类或谱聚类)来实现。
方法2a:层次聚类+自定义距离
import numpy as np from sklearn.cluster import AgglomerativeClustering from sklearn.metrics.pairwise import cosine_distances X = np.array([ [0, -1, 1, 0], [0, -1, 1, 0], [0, 1, -1, 0], [1, 1, -1, -1], [-1, -1, 1, 1] ]) # 自定义距离:1 - |余弦相似度|(余弦相似度=1 - cosine_distances) def cosine_abs_distance(X): cosine_sim = 1 - cosine_distances(X) return 1 - np.abs(cosine_sim) # 计算距离矩阵 distance_matrix = cosine_abs_distance(X) # 用层次聚类 cluster = AgglomerativeClustering(n_clusters=2, affinity='precomputed', linkage='average') cluster_labels = cluster.fit_predict(distance_matrix) print("聚类结果标签:", cluster_labels)
方法2b:谱聚类(适合复杂数据)
谱聚类基于相似度矩阵进行聚类,能更好地处理非线性的聚类结构:
import numpy as np from sklearn.cluster import SpectralClustering from sklearn.metrics.pairwise import cosine_distances X = np.array([ [0, -1, 1, 0], [0, -1, 1, 0], [0, 1, -1, 0], [1, 1, -1, -1], [-1, -1, 1, 1] ]) # 构建相似度矩阵:取余弦相似度的绝对值 similarity_matrix = np.abs(1 - cosine_distances(X)) # 谱聚类 spectral = SpectralClustering(n_clusters=2, affinity='precomputed', random_state=42) cluster_labels = spectral.fit_predict(similarity_matrix) print("聚类结果标签:", cluster_labels)
优缺点
- ✅ 更通用,不依赖向量的元素位置,直接基于方向判断
- ❌ 层次聚类和谱聚类的计算复杂度比KMeans高,大数据集下速度较慢
总结
如果你的数据量不大,解决方案1是最便捷的选择;如果数据复杂或希望更通用的方向匹配,解决方案2会更合适。这两种方法都能让聚类算法识别出反相关的行,把它们归为同一聚类。
内容的提问来源于stack exchange,提问作者JVonKorff

