我有通过K-means算法聚类后的数据,以及这些聚类的中心点。我想计算每个聚类中心的密度,并移除密度最高的那个聚类。我做了研究,发现了这个公式。
N(c) 是聚类c的邻居聚类中心的集合,应该是5个。我尝试实现这个算法但没能成功。你能帮我实现吗?
这是我目前的代码:
df = make_blobs(n_samples=5000, n_features=15,centers=15, cluster_std=1,random_state=10)X,y=dfX_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=10)TrainData=X_train,y_trainn_clusters_sampling=10 kmeans2 = KMeans(n_clusters = n_clusters_sampling,random_state=10)kmeans2.fit(X_train)centroids = kmeans2.cluster_centers_
回答:
你的问题实质上是在由中心点组成的“新数据集”上进行“k最近邻搜索”。你需要找到每个中心点的5个最近邻点及其关联距离。幸运的是,sklearn
确实有NearestNeighbors类来提供这种功能:
...centroids = kmeans2.cluster_centers_from sklearn.neighbors import NearestNeighborsnn = NearestNeighbors(n_neighbors=6) # 6不是打错字。下文解释。nn.fit(centroids)neigh_dist, neigh_ind = nn.kneighbors(centroids, return_distance=True)densities = [5/np.sum(neigh_dist[i,:]) for i in range(centroids.shape[0])]print(densities)
请注意,我们用相同的点(中心点)来拟合nn
对象和执行查询。这就是为什么n_neighbors
是6的原因:对于每个中心点,它本身将是最近的邻居,距离为零。
当return_distance
设为True时,.kneighbors()
方法会(也)返回一个形状为(n
,n_neighbors
)的距离数组,其中n
是查询点的数量,即中心点。该数组的第i行第j列告诉您第j个邻居与第i个中心点的距离。因此,我们按行计算平均值以按照你发布的公式计算密度。
编辑:答案的下一部分解决了原帖主关于移除密度最高聚类的问题的评论。
移除一个聚类,比如说c
,实质上意味着将其数据点的聚类标签重新分配给下一个最接近的中心点。所以,现在我们有一个新的1最近邻问题,我们可以再次使用我们已经创建的NearestNeihbors对象。
我们对最初分配给c
的点的“中心点数据集”执行2最近邻搜索。
第一个邻居当然是c
,所以我们只保留第二个最近邻。
然后我们简单地用新的索引更新这些数据点的原始分配表。
# 运行k-means并获取初始聚类分配的数组assignments = kmeans2.predict(X_train)# 找到要移除的聚类的索引c = np.argmax(densities)# 对于最初分配给c的每个点,找到其最接近的中心点。# 我们再次使用搜索一个额外邻居的技巧,因为我们知道这些点的最近中心点将是c.nearest_centroids = nn.kneighbors(X_train[assignments==c,:], n_neighbors=2, return_distance=False)# 获取新的最接近的中心点(即数组的第二列)并使其成为一维nearest_centroids = nearest_centroids[:,1].flatten()# 简单地更新特定数据点的初始分配表assignments[assignments==c] = nearest_centroids
assignments
数组现在不包含c
的值。请注意,这可能会在绘图或进行结果的后处理时留下“空洞”,因为会有一个没有点分配给它的聚类。如果你想避免这种情况,只需将大于c的索引减一:
assignments = np.array([i-1 if i>c else i for i in assignments])
如果你还想移除中心点本身:
centroids = np.delete(centroids, c, axis=0) # 通过索引从numpy数组中移除行