如何自动拆分点数超最大值的大型HDBSCAN聚类?
解决大聚类拆分问题的方案
针对HDBSCAN生成的超大规模聚类,结合你无法使用K-Means(依赖欧氏距离)但有最短路径距离矩阵的情况,可以通过对大聚类单独使用支持自定义距离矩阵的聚类算法进行二次拆分来实现目标,以下是具体步骤和代码:
核心思路
- 筛选出规模超过指定最大值的聚类
- 对每个大聚类,利用已有的最短路径距离矩阵,使用支持预计算距离的聚类算法(如层次聚类、DBSCAN)进行拆分
- 合并拆分后的子聚类标签,生成全局唯一的子聚类标识
完整代码实现
import geopandas as gpd import pandas as pd import numpy as np import hdbscan import matplotlib.pyplot as plt from sklearn.cluster import AgglomerativeClustering # 若选择DBSCAN密度拆分,取消下方注释 # from sklearn.cluster import DBSCAN # 1. 读取数据并完成初始HDBSCAN聚类 cl = gpd.read_file("cluster.gpkg") df = pd.concat([cl.geometry.x, cl.geometry.y], axis=1) mins = 40 min_size = 400 max_size = 400 # 你指定的最大聚类规模 clusterer = hdbscan.HDBSCAN(min_cluster_size=min_size, min_samples=mins, allow_single_cluster=True) clusterer.fit(df.iloc[:, 0:2]) df['cluster'] = clusterer.labels_ df['subcluster'] = df['cluster'].copy() # 初始化子聚类为原聚类标签 # 2. 准备最短路径距离矩阵(需替换为你已有的全局距离矩阵,维度与df行数匹配) # dist_matrix = 你的最短路径距离矩阵(形状:(len(df), len(df))) # 3. 遍历聚类,拆分超大规模集群 unique_clusters = df['cluster'].unique() subcluster_counter = df['cluster'].max() + 1 # 子聚类标签从原最大标签+1开始 for cluster_label in unique_clusters: if cluster_label == -1: # 跳过噪声点 continue cluster_subset = df[df['cluster'] == cluster_label] cluster_size = len(cluster_subset) if cluster_size <= max_size: continue # 规模符合要求,无需拆分 # 提取当前大聚类对应的子距离矩阵 subset_indices = cluster_subset.index subset_dist_matrix = dist_matrix[subset_indices, :][:, subset_indices] # 方法1:层次聚类——按指定规模拆分(严格控制子聚类大小) num_subclusters = np.ceil(cluster_size / max_size).astype(int) agg_clustering = AgglomerativeClustering( n_clusters=num_subclusters, affinity='precomputed', linkage='single' # 单链接逻辑与HDBSCAN贴近,保留密度特性 ) sub_labels = agg_clustering.fit_predict(subset_dist_matrix) # 方法2:DBSCAN——基于密度拆分(需调整eps控制子聚类大小) # dbscan = DBSCAN(eps=你的距离阈值, min_samples=mins, metric='precomputed') # sub_labels = dbscan.fit_predict(subset_dist_matrix) # 注意:DBSCAN可能生成噪声,可将噪声点合并到最近的子聚类 # 将子聚类标签映射为全局唯一标识 global_sub_labels = [subcluster_counter + label for label in sub_labels] df.loc[subset_indices, 'subcluster'] = global_sub_labels subcluster_counter += num_subclusters # 4. 可视化初始聚类与拆分结果 fig, ax = plt.subplots(1, 2, figsize=(20, 6)) # 左图:初始HDBSCAN聚类 unique_labels = np.unique(df['cluster']) for label in unique_labels: subset = df[df['cluster'] == label] if label == -1: ax[0].scatter(subset.iloc[:, 0], subset.iloc[:, 1], label="噪声", color='gray', marker=".", alpha=0.3) else: ax[0].scatter(subset.iloc[:, 0], subset.iloc[:, 1], label=f'聚类 {label}', marker=".", alpha=1) ax[0].set_xlabel('经度') ax[0].set_ylabel('纬度') ax[0].set_title('初始HDBSCAN聚类结果') ax[0].legend() # 右图:拆分后的子聚类 unique_sub_labels = np.unique(df['subcluster']) for label in unique_sub_labels: subset = df[df['subcluster'] == label] if label == -1: ax[1].scatter(subset.iloc[:, 0], subset.iloc[:, 1], label="噪声", color='gray', marker=".", alpha=0.3) else: ax[1].scatter(subset.iloc[:, 0], subset.iloc[:, 1], marker=".", alpha=1) ax[1].set_xlabel('经度') ax[1].set_ylabel('纬度') ax[1].set_title('拆分后的子聚类结果') ax[1].legend() plt.tight_layout() plt.show()
关键说明
- 距离矩阵处理:确保最短路径距离矩阵为对称矩阵,维度与数据行匹配;若矩阵过大,仅提取当前大聚类的子矩阵可节省计算资源
- 算法选择:
- 层次聚类:能精确按指定数量拆分聚类,适合需要严格控制规模的场景,
linkage='single'的逻辑与HDBSCAN更适配 - DBSCAN:基于密度拆分,需调整
eps参数控制子聚类大小,可能生成少量噪声,需额外处理
- 层次聚类:能精确按指定数量拆分聚类,适合需要严格控制规模的场景,
- 标签管理:通过全局计数器避免子聚类标签与原聚类标签冲突
内容的提问来源于stack exchange,提问作者Ben Hendel
相关产品推荐
相关产品推荐

