如何为scikit-learn层次聚类添加指定列值相同才可聚类的约束
实现约束层次聚类的两种方案
方案1:按约束列分组独立聚类(推荐)
这种方法逻辑最简单,完全满足你的约束要求,且支持原参数中的ward linkage规则:
- 操作逻辑:先按照指定约束列的取值将数据集拆分,每个取值对应的子集单独执行层次聚类,最后合并所有子集的聚类结果即可,天然不会出现跨取值聚到同一簇的情况。
- 实现代码:
import pandas as pd from sklearn.cluster import AgglomerativeClustering # 保留你原有的因子化处理逻辑 z = df.apply(lambda x: pd.factorize(x)[0]) # 指定约束列:你说的第3列对应0基索引为2,可根据实际列索引调整 constraint_col = 2 result = [] # 按约束列取值遍历分组 for group_val, group_df in z.groupby(z.iloc[:, constraint_col]): # 每组单独执行聚类,参数和你原设置完全一致 cluster = AgglomerativeClustering( n_clusters=None, distance_threshold=10, affinity='euclidean', linkage='ward' ) group_labels = cluster.fit_predict(group_df) # 给不同分组的簇标签加上前缀避免重复 group_labels = [f"{group_val}_{lab}" for lab in group_labels] # 记录原索引和对应标签 result.extend(zip(group_df.index, group_labels)) # 按原数据集索引排序,得到和原顺序一致的聚类标签 result.sort() final_cluster_labels = [item[1] for item in result]
方案2:自定义距离矩阵实现约束
如果你不想拆分数据集,可以通过修改距离矩阵的方式强制约束,不同约束列取值的样本距离设为无穷大,聚类时永远不会合并:
- 注意:该方案不能使用
wardlinkage,因为ward规则仅支持输入原始特征计算方差,需要替换为complete/average等其他linkage规则。 - 实现代码:
import pandas as pd import numpy as np from sklearn.cluster import AgglomerativeClustering from sklearn.metrics.pairwise import euclidean_distances z = df.apply(lambda x: pd.factorize(x)[0]) constraint_col = 2 # 计算原始欧氏距离矩阵 dist_matrix = euclidean_distances(z) constraint_values = z.iloc[:, constraint_col].values # 生成掩码:约束列取值不同的样本对标记为True mask = constraint_values[:, np.newaxis] != constraint_values[np.newaxis, :] # 把不符合约束的样本对距离设为无穷大 dist_matrix[mask] = np.inf # 聚类时指定用预计算的距离矩阵,更换linkage规则 cluster = AgglomerativeClustering( n_clusters=None, distance_threshold=10, affinity='precomputed', linkage='complete' ) final_cluster_labels = cluster.fit_predict(dist_matrix)
内容的提问来源于stack exchange,提问作者Murcie
相关产品推荐
相关产品推荐

