在大小相等的 k 个集群中对 n 个点进行分组

Posted

技术标签:

【中文标题】在大小相等的 k 个集群中对 n 个点进行分组【英文标题】:Group n points in k clusters of equal size [duplicate] 【发布时间】:2012-02-06 11:24:46 【问题描述】:

可能重复:K-means algorithm variation with equal cluster size

编辑:像 casperOne 向我指出这个问题是重复的。无论如何,这里有一个更笼统的问题,涵盖了这个问题:https://stats.stackexchange.com/questions/8744/clustering-procedure-where-each-cluster-has-an-equal-number-of-points

我的要求

在一个项目中,我需要将 n 个点 (x,y) 分组到 k 个大小相等 (n / k) 的集群中。其中 x 和 y 是双浮点数,n 的取值范围为 100 到 10000,k 的取值范围为 2 到 100。在算法运行之前 k 也是已知的。

我的实验

我开始使用http://en.wikipedia.org/wiki/K-means_clustering 算法来解决这个问题,该算法可以很好地快速生成大小大致相同的 k 个集群。

但我的问题是,K-means 产生大小大致相同的集群,我需要集群的大小完全相同(或更准确地说:我需要它们的大小在 floor(n / k) 和 ceil(n / k))。

在你向我指出之前,是的,我在这里尝试了第一个答案K-means algorithm variation with equal cluster size,这听起来是个好主意。

主要思想是通过K-means对簇产生的数组进行后处理。从最大的集群到最小的集群。我们通过将额外的点移动到另一个最近的集群来减少具有超过 n / k 个成员的集群的大小。更不用说已经减少的集群了。

这是我实现的伪代码:

n is the number of point
k is the number of cluster
m = n / k (the ideal cluster size)
c is the array of cluster after K-means
c' = c sorted by size in descending order
for each cluster i in c' where i = 1 to k - 1
    n = size of cluster i - m (the number of point to move)
    loop n times
        find a point p in cluster i with minimal distance to a cluster j in c' where j > i
        move point p from cluster i to cluster j
    end loop
    recalculate centroids
end for each

这个算法的问题是在接近过程结束时(当 i 接近 k 时),我们必须在 c' 中选择一个簇 j(其中 j > i 因为我们需要不理会已经处理过的簇),但是我们发现的这个簇 j 可能离簇 i 很远,从而打破了簇的概念。

我的问题

是否有可以满足我要求的后 K-means 算法或 K-means 变体,还是我从一开始就错了,我需要寻找其他聚类算法?

PS:我不介意自己实现解决方案,但如果我可以使用库,最好是在 JAVA 中。

【问题讨论】:

如何选择初始集群? 簇的数量和它们的初始质心由用户(人类)选择。 @casperOne,您将此问题作为重复问题关闭了吗?我实际上在我的问题中说过,我在***.com/questions/5452576/… 中尝试了建议的解决方案但没有成功,我试图在这里更进一步,询问是否有其他解决方案。但是,如果您决定将其关闭,我不会生您的气 :) 我只是不认为它是重复的。 @Pierre-DavidBelanger:您的问题没有很好地将自己与其他问题区分开来。如果您可以进行重大更改以指出差异(在问题中,而不是 cmets),那么它可以重新打开。另外,您是否考虑过在stats.stackexchange.com 上提问?鉴于该网站的存在,这个问题有点离题。 您是否尝试了现有问题的所有答案?例如elki-project.github.io/tutorial/same-size_k_means 的教程?是java,看来满足你的要求了? 【参考方案1】:

试试这个 k-means 变体:

初始化

从数据集中随机选择k中心,或者使用kmeans++策略更好 对于每个点,计算到其最近的聚类中心的距离,并为此构建一个堆 从堆中提取点,并将它们分配给最近的集群,除非集群已经满了。如果是这样,计算下一个最近的集群中心并重新插入到堆中

最后,您应该有一个满足您对每个集群 +-1 相同数量的对象的要求的分区(确保最后几个集群也有正确的数量。第一个 m 集群应该有 @987654323 @ 对象,其余的正好是 floor 对象。) 请注意,使用堆可确保集群保持凸:如果它们不再是凸的,则会有更好的交换候选。

迭代步骤

必要条件:每个集群的列表,其中包含“交换建议”(希望位于不同集群中的对象)。

E 步骤:按照常规 k-means 计算更新后的聚类中心

M 步骤:遍历所有点(仅一个点,或一批中的所有点)

计算离对象最近的集群中心/比当前集群更近的所有集群中心。如果是不同的集群:

如果其他集群小于当前集群,只需将其移至新集群 如果有来自另一个集群(或任何距离更短的集群)的交换提议,则交换两个元素集群分配(如果有多个提议,则选择改进最大的一个) 否则,为其他集群指明交换建议

集群大小保持不变(+- ceil/floor 差异),一个对象只从一个集群移动到另一个集群,只要它导致估计的改进。因此,它应该像 k-means 一样在某个点收敛。不过它可能会慢一些(即更多的迭代)。

我不知道这是否已经发布或实施过。这正是我会尝试的(如果我会尝试 k-means。有更好的聚类算法。)

【讨论】:

有趣!我喜欢你关于集群“交换提案”列表的想法。我会试试的。另外,您说“有更好的聚类算法”:我不受 K-means 的约束,我非常愿意尝试其他/更好的聚类算法来帮助我满足我的要求(n 分在 k 个大小相等的簇中)。那么,您能否指出一些可以生成相同大小集群的更好的聚类算法 我不知道有谁可以胜任这项特定任务。到目前为止,我还不需要相同的大小。 在我看来,您希望确保点不被分配给不相邻的集群,以便生成的 Voronoi 单元仍然是凸的,但我不认为您的算法这样做。 只要它首先移动最好的候选人,这不是问题。如果集群不再是凸的,就会有更好的切换候选者。但也许我应该强调,显然应该移动收益最大的候选人。 请注意,初始化步骤不保证凸性。堆只是确保添加到集群中的每个点都比所有其他点更远,但这很可能会产生凹缝甚至不连贯的块。【参考方案2】:

不是该主题的专家,我曾经需要想出一个简单的算法来对地图上的位置进行聚类,其中每个点都需要成为聚类的一部分,并且聚类以多种方式绑定(不仅仅是在大小(即点数),但在其他一些取决于不同因素的措施中也是如此)。

通过首先找到“困难”点,然后从那里增长集群,我得到了最好的结果。 “困难”点将是难以达到的点,例如因为它们会单独位于整个区域的郊区,或者因为它们比其他点更能帮助达到另一个集群边界条件。这有助于整齐排列集群,只剩下很少的孤独者和相应的手工来放置它们。

如果您当前的算法通常会最后找到这些难点,这可能会对您有所帮助。

【讨论】:

有趣。实际上,当用户将初始质心放在那些“困难”点上时(因此它们首先被添加到一个簇中,并且它们的簇从那里增长),K-means 能够产生一个很好的簇布局,但遗憾的是这些簇又是不同大小(点数)。我已经测试过了。谢谢你。您是否使用现有的库/算法来满足您的需求? 不,我只是手动快速地实现了它,它最初只是一个简单的概念证明,性能没有问题。您应该逐点增长集群,即在每一轮中,将每个集群增长一个。那么点数不能发散。 哈哈,是的,我试过了:在一个循环中将所有集群增长一个最近的点,直到没有一个点是单独的。这导致了以下问题(接近算法的末尾):假设我们要放置最后一个点,该点靠近集群 A 的左侧,但集群 A 已经满了,所以我们必须选择集群 B在集群 A 的右侧,B 现在将包含一个应该属于 A 的点。 这听起来很容易通过简单的后处理步骤解决。比如:在 A 和 B 之间的边界上,分别找到与集群 A 和 B 的链接最薄弱的点,如果这样会增加整体能量,就让它们换边。您可以计算达到最高能量点所需的开关数量,以便对算法的第一步进行基准测试。

以上是关于在大小相等的 k 个集群中对 n 个点进行分组的主要内容,如果未能解决你的问题,请参考以下文章

在 R 中,是不是有一种算法可以创建大小大致相等的集群

如何创建大小相等的集群

具有最大集群大小的集群

根据与 LDA 主题/特征集群的相似性对文本进行分组

在 Python 中对时间序列数据进行聚类

[具有相同簇大小的K均值算法变异