我在有一些点,并且我想将这些点聚类,以便:
每个簇包含相等数量的元素。(假设簇数除以。)
每个聚类在某种意义上都是“空间内聚的”,就像来自均值的聚类一样。
很容易想到很多满足其中一个或另一个要求的聚类过程,但是没有人知道同时获得两者的方法吗?
我在有一些点,并且我想将这些点聚类,以便:
每个簇包含相等数量的元素。(假设簇数除以。)
每个聚类在某种意义上都是“空间内聚的”,就像来自均值的聚类一样。
很容易想到很多满足其中一个或另一个要求的聚类过程,但是没有人知道同时获得两者的方法吗?
Answers:
尝试以下k均值变体:
初始化:
k
从数据集中随机选择中心,或者使用kmeans ++策略更好地选择中心最后,您应该进行分区,以满足每个群集+ -1个相同数量的对象的要求(确保最后几个群集也具有正确的数量。第一个m
群集应该具有ceil
对象,其余的完全是floor
对象。)
迭代步骤:
必要条件:每个群集的列表,其中包含“交换提案”(希望放在其他群集中的对象)。
E步骤:按照常规k均值计算更新的聚类中心
M步:遍历所有点(仅一批或全部一批)
计算距对象最近的群集中心/比当前群集更近的所有群集中心。如果是其他群集:
聚类大小保持不变(+-天花板/地板差),对象仅从一个聚类移动到另一聚类,只要它能改善估计值即可。因此,它应该在某点收敛,例如k均值。不过,它可能会更慢(即更多迭代)。
我不知道它是否已经发布或实施过。这就是我会尝试的(如果我尝试k-means。会有更好的聚类算法。)
一个很好的起点可能是ELKI中的k-means实现,它似乎已经支持三种不同的初始化(包括k-means ++),并且作者说他们还希望拥有不同的迭代策略,以涵盖所有常见的模块化方式的变体(例如Lloyd,MacQueen等)。
这是一个优化问题。我们有一个开放源代码的Java库来解决此问题(聚簇每个集群的数量必须在设置范围之间)。但是,您需要的总点数最多为几千个-不超过5000或10000。
图书馆在这里:
https://github.com/PGWelch/territorium/tree/master/territorium.core
该库本身是针对地理/ GIS类型问题而设置的-因此您将看到对X和Y,纬度和经度,客户,距离和时间等的引用。您可以忽略“地理”元素,而将其用作纯集群器。
您提供了一组初始为空的输入集群,每个集群具有最小和最大目标数量。聚类器将使用基于启发式的优化算法(交换,移动等)将点分配给您的输入聚类。在优化中,它首先优先考虑将每个群集保持在其最小和最大数量范围内,然后其次将群集中所有点与群集中心点之间的距离最小化,因此群集在空间上具有凝聚力。
您可以使用此接口为求解器提供点之间的度量函数(即距离函数):
该度量标准实际上是为了返回距离和“时间”而构造的,因为它是针对基于旅行的地理问题而设计的,但是对于任意聚类问题,只需将“时间”设置为零,而距离是您要在之间使用的实际度量标准点。
您将在此类中设置您的问题:
您的积分将是“客户”,其数量将是1。在客户类别中,假设您要在TravelMatrix返回的“距离”字段中返回度量距离,请确保您设置costPerUnitTime = 0和costPerUnitDistance = 1。
请参阅此处以了解运行求解器的示例:
我建议最近发表论文《基于正则化信息最大化的判别聚类》(及其中的参考文献)。具体而言,第2节讨论类平衡和聚类假设。
最近,我自己需要一个不太大的数据集。我的回答尽管运行时间相对较长,但可以保证收敛到局部最优值。
def eqsc(X, K=None, G=None):
"equal-size clustering based on data exchanges between pairs of clusters"
from scipy.spatial.distance import pdist, squareform
from matplotlib import pyplot as plt
from matplotlib import animation as ani
from matplotlib.patches import Polygon
from matplotlib.collections import PatchCollection
def error(K, m, D):
"""return average distances between data in one cluster, averaged over all clusters"""
E = 0
for k in range(K):
i = numpy.where(m == k)[0] # indeces of datapoints belonging to class k
E += numpy.mean(D[numpy.meshgrid(i,i)])
return E / K
numpy.random.seed(0) # repeatability
N, n = X.shape
if G is None and K is not None:
G = N // K # group size
elif K is None and G is not None:
K = N // G # number of clusters
else:
raise Exception('must specify either K or G')
D = squareform(pdist(X)) # distance matrix
m = numpy.random.permutation(N) % K # initial membership
E = error(K, m, D)
# visualization
#FFMpegWriter = ani.writers['ffmpeg']
#writer = FFMpegWriter(fps=15)
#fig = plt.figure()
#with writer.saving(fig, "ec.mp4", 100):
t = 1
while True:
E_p = E
for a in range(N): # systematically
for b in range(a):
m[a], m[b] = m[b], m[a] # exchange membership
E_t = error(K, m, D)
if E_t < E:
E = E_t
print("{}: {}<->{} E={}".format(t, a, b, E))
#plt.clf()
#for i in range(N):
#plt.text(X[i,0], X[i,1], m[i])
#writer.grab_frame()
else:
m[a], m[b] = m[b], m[a] # put them back
if E_p == E:
break
t += 1
fig, ax = plt.subplots()
patches = []
for k in range(K):
i = numpy.where(m == k)[0] # indeces of datapoints belonging to class k
x = X[i]
patches.append(Polygon(x[:,:2], True)) # how to draw this clock-wise?
u = numpy.mean(x, 0)
plt.text(u[0], u[1], k)
p = PatchCollection(patches, alpha=0.5)
ax.add_collection(p)
plt.show()
if __name__ == "__main__":
N, n = 100, 2
X = numpy.random.rand(N, n)
eqsc(X, G=3)