我正在寻找最快的算法,用于按地点将地图上的点分组为相同大小的组。 k-means clustering algorithm看起来很简单,很有希望,但不会产生同样大小的群体。
是否存在此算法的变体或不同的算法允许所有群集的成员数相等?
答案 0 :(得分:12)
这可能有效:应用Lloyd's algorithm来获取 k 质心。通过降低数组中关联簇的大小来对质心进行排序。对于 i = 1到 k -1,推送群集 i 中的数据点,与任何其他质心 j的距离最小( i &lt; j ≤ k )关闭 j 并重新计算质心 i < / em>(但不要重新计算群集),直到群集大小为 n / k 。
此后处理步骤的复杂性为O( k ² n lg n )。
答案 1 :(得分:5)
您可以将距离视为定义加权图。相当多的图分区算法明确地基于尝试将图顶点分割成两组相等的大小。例如,使用拉普拉斯算子出现在Kernighan-Lin algorithm和spectral graph partitioning中。要获得多个集群,您可以递归地应用分区算法;在频谱图分区的链接上有一个很好的讨论。
答案 2 :(得分:5)
ELKI数据挖掘框架有tutorial on equal-size k-means。
这不是一个特别好算法,但它是一个足够简单的k-means变体来编写教程并教人们如何实现他们自己的聚类算法变体;并且显然有些人确实需要他们的集群具有相同的大小,尽管SSQ质量将比常规k均值更差。
在ELKI 0.7.5中,您可以选择此算法为tutorial.clustering.SameSizeKMeansAlgorithm
。
答案 3 :(得分:3)
尝试这种k-means变体:
<强>初始化强>:
k
个中心,甚至更好地使用kmeans ++策略最后,你应该有一个分区,满足你对每个集群+ -1相同数量的对象的要求(确保最后几个集群也有正确的数字。第一个m
集群应该有ceil
个对象,其余为floor
个对象。)
迭代步骤:
必需条件:每个群集的列表都包含“交换提议”(更喜欢位于不同群集中的对象)。
E 步骤:计算更新的群集中心,如常规k-means
M 步骤:迭代所有点(一个或一个中只有一个)
计算距离当前群集较近的对象/所有群集中心的最近群集中心。如果它是一个不同的集群:
群集大小保持不变(+ - 小区/楼层差异),对象仅从一个群集移动到另一个群集,只要它导致估计的改进。因此它应该像k-means一样收敛。但它可能会慢一些(即更多迭代)。
我不知道之前是否已经发布或实施过。这就是我想尝试的(如果我尝试k-means。有更好的聚类算法。)
答案 4 :(得分:2)
考虑某种形式的递归贪婪合并 - 每个点以单个群集开始并重复合并最接近的两个,使得这样的合并不超过最大值。尺寸。如果您别无选择但超过最大尺寸,则在本地重新集群。这是一种回溯层次聚类形式:http://en.wikipedia.org/wiki/Hierarchical_clustering
答案 5 :(得分:2)
给定群集质心,有一个更清晰的后处理。让N
为项目数,K
群集数量和S = ceil(N/K)
最大群集大小。
(item_id, cluster_id, distance)
(item_id, cluster_id, distance)
:
cluster_id
中的元素数量超过S
则不执行任何操作item_id
添加到群集cluster_id
。这在O(NK lg(N))中运行,应该给@larsmans解决方案提供可比较的结果,并且更容易实现。在伪python中
dists = []
clusts = [None] * N
counts = [0] * K
for i, v in enumerate(items):
dist = map( lambda x: dist(x, v), centroids )
dd = map( lambda (k, v): (i, k, v), enumerate(dist) )
dists.extend(dd)
dists = sorted(dists, key = lambda (x,y,z): z)
for (item_id, cluster_id, d) in dists:
if counts[cluster_id] >= S:
continue
if clusts[item_id] == None:
clusts[item_id] = cluster_id
counts[cluster_id] = counts[cluster_id] + 1
答案 6 :(得分:2)
在阅读了这个问题和几个类似的问题之后,我使用https://elki-project.github.io/tutorial/same-size_k_means上的Elki教程创建了一个相同大小的k-means的python实现,该教程利用scikit-learn的K-Means实现了大多数常用方法和熟悉的API。
我的实施在这里找到: https://github.com/ndanielsen/Same-Size-K-Means
在此函数中可以找到聚类逻辑:_labels_inertia_precompute_dense()
答案 7 :(得分:1)
2012年1月新增:
比后处理更好的是保持簇大小
和他们一样大致相同。
例如,找到每个X最近的3个中心,
然后将X添加到最好的那个
距离 - λclustersize。
如果k-means的集群大小相等,那么k-means后的简单贪婪后处理可能就足够了。
(这近似于k-means对Npt x C距离矩阵的分配算法。)
可以迭代
diffsizecentres = kmeans( X, centres, ... )
X_centre_distances = scipy.spatial.distance.cdist( X, diffsizecentres )
# or just the nearest few centres
xtoc = samesizeclusters( X_centre_distances )
samesizecentres = [X[xtoc[c]].mean(axis=0) for c in range(k)]
...
如果迭代改变了中心,我会感到惊讶, 但它依赖于。
关于你的Npoint Ncluster和Ndim有多大?
#!/usr/bin/env python
from __future__ import division
from operator import itemgetter
import numpy as np
__date__ = "2011-03-28 Mar denis"
def samesizecluster( D ):
""" in: point-to-cluster-centre distances D, Npt x C
e.g. from scipy.spatial.distance.cdist
out: xtoc, X -> C, equal-size clusters
method: sort all D, greedy
"""
# could take only the nearest few x-to-C distances
# add constraints to real assignment algorithm ?
Npt, C = D.shape
clustersize = (Npt + C - 1) // C
xcd = list( np.ndenumerate(D) ) # ((0,0), d00), ((0,1), d01) ...
xcd.sort( key=itemgetter(1) )
xtoc = np.ones( Npt, int ) * -1
nincluster = np.zeros( C, int )
nall = 0
for (x,c), d in xcd:
if xtoc[x] < 0 and nincluster[c] < clustersize:
xtoc[x] = c
nincluster[c] += 1
nall += 1
if nall >= Npt: break
return xtoc
#...............................................................................
if __name__ == "__main__":
import random
import sys
from scipy.spatial import distance
# http://docs.scipy.org/doc/scipy/reference/spatial.distance.html
Npt = 100
C = 3
dim = 3
seed = 1
exec( "\n".join( sys.argv[1:] )) # run this.py N= ...
np.set_printoptions( 2, threshold=200, edgeitems=5, suppress=True ) # .2f
random.seed(seed)
np.random.seed(seed)
X = np.random.uniform( size=(Npt,dim) )
centres = random.sample( X, C )
D = distance.cdist( X, centres )
xtoc = samesizecluster( D )
print "samesizecluster sizes:", np.bincount(xtoc)
# Npt=100 C=3 -> 32 34 34
答案 8 :(得分:1)
最近我自己需要一个不是很大的数据集。我的答案虽然运行时间相对较长,但可以保证收敛到局部最优值。
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)
答案 9 :(得分:1)
一般而言,按照距离将地图上的点分组为大小相同的组是理论上不可能完成的任务。因为分组指向大小相同的组与按距离对群集中的点进行分组冲突。
看到这个情节:
有四点:
A.[1,1]
B.[1,2]
C.[2,2]
D.[5,5]
如果我们将这些点聚类成两个聚类。显然,(A,B,C)将是簇1,D将是簇2.但是如果我们需要相同大小的组,(A,B)将是一个簇,(C,D)将是另一个簇。这违反了群集规则,因为C更接近(A,B)的中心,但它属于群集(C,D)。因此,不能同时满足群集和同等大小的群体的要求。
答案 10 :(得分:0)
在集群分配期间,还可以在距离上增加一个“频率惩罚”,这使得高频集群更难获得额外的分数。这在“Frequency Sensitive Competitive Learning for 高维超球面的平衡聚类 - Arindam Banerjee 和 Joydeep Ghosh - IEEE Transactions on Neural Networks"
http://www.ideal.ece.utexas.edu/papers/arindam04tnn.pdf
他们也有在线/流媒体版本。
答案 11 :(得分:0)
万一有人想在这里复制并粘贴一个简短的函数-基本运行KMeans,然后在分配给簇的最大点(簇大小)的约束下找到点与簇的最小匹配
from sklearn.cluster import KMeans
from scipy.spatial.distance import cdist
from scipy.optimize import linear_sum_assignment
import numpy as np
def get_even_clusters(X, cluster_size):
n_clusters = int(np.ceil(len(X)/cluster_size))
kmeans = KMeans(n_clusters)
kmeans.fit(X)
centers = kmeans.cluster_centers_
centers = centers.reshape(-1, 1, X.shape[-1]).repeat(cluster_size, 1).reshape(-1, X.shape[-1])
distance_matrix = cdist(X, centers)
clusters = linear_sum_assignment(distance_matrix)[1]//cluster_size
return clusters
答案 12 :(得分:0)
相等大小的k均值是约束k均值过程的特例,其中每个聚类必须具有最少数量的点。这个问题可以表述为图问题,其中节点是要聚类的点,每个点都有每个质心的边缘,其中边缘权重是到质心的平方欧几里德距离。在这里讨论:
Bradley PS,Bennett KP,Demiriz A(2000),Constrained K-Means Clustering。微软研究院。
here提供了Python实现。
答案 13 :(得分:0)
我一直在努力解决这个问题。但是,我意识到我一直使用错误的关键字。如果您希望点结果成员的数量大小相同,那么您正在进行分组,而不是群集。我终于能够使用简单的python脚本和postgis查询来解决问题。
例如,我有一个名为tb_points的表,它有4000个坐标点,你想把它分成10个相同大小的组,每组包含400个坐标点。以下是表结构的示例
CREATE TABLE tb_points (
id SERIAL PRIMARY KEY,
outlet_id INTEGER,
longitude FLOAT,
latitide FLOAT,
group_id INTEGER
);
然后你需要做的是:
这是python中的实现:
import psycopg2
dbhost = ''
dbuser = ''
dbpass = ''
dbname = ''
dbport = 5432
conn = psycopg2.connect(host = dbhost,
user = dbuser,
password = dbpass,
database = dbname,
port = dbport)
def fetch(sql):
cursor = conn.cursor()
rs = None
try:
cursor.execute(sql)
rs = cursor.fetchall()
except psycopg2.Error as e:
print(e.pgerror)
rs = 'error'
cursor.close()
return rs
def execScalar(sql):
cursor = conn.cursor()
try:
cursor.execute(sql)
conn.commit()
rowsaffected = cursor.rowcount
except psycopg2.Error as e:
print(e.pgerror)
rowsaffected = -1
conn.rollback()
cursor.close()
return rowsaffected
def select_first_cluster_id():
sql = """ SELECT a.outlet_id as ori_id, a.longitude as ori_lon,
a.latitude as ori_lat, b.outlet_id as dest_id, b.longitude as
dest_lon, b.latitude as dest_lat,
ST_Distance(CAST(ST_SetSRID(ST_Point(a.longitude,a.latitude),4326)
AS geography),
CAST(ST_SetSRID(ST_Point(b.longitude,b.latitude),4326) AS geography))
AS air_distance FROM tb_points a CROSS JOIN tb_points b WHERE
a.outlet_id != b.outlet_id and a.group_id is NULL and b.group_id is
null order by air_distance desc limit 1 """
return sql
def update_group_id(group_id, ori_id, limit_constraint):
sql = """ UPDATE tb_points
set group_id = %s
where outlet_id in
(select b.outlet_id
from tb_points a,
tb_points b
where a.outlet_id = '%s'
and a.group_id is null
and b.group_id is null
order by ST_Distance(CAST(ST_SetSRID(ST_Point(a.longitude,a.latitude),4326) AS geography),
CAST(ST_SetSRID(ST_Point(b.longitude,b.latitude),4326) AS geography)) asc
limit %s)
""" % (group_id, ori_id, limit_constraint)
return sql
def clustering():
data_constraint = [100]
n = 1
while n <= 10:
sql = select_first_cluster_id()
res = fetch(sql)
ori_id = res[0][0]
sql = update_group_id(n, ori_id, data_constraint[0])
print(sql)
execScalar(sql)
n += 1
clustering()
希望有所帮助
答案 14 :(得分:0)
另请查看K-d树,该树对数据进行分区,直到每个分区的成员都小于作为算法输入的BUCKET_SIZE。
这不会强制桶/分区的大小完全相同,但它们都会小于BUCKET_SIZE。
答案 15 :(得分:0)
我谦虚地建议您尝试这个项目ekmeans。
Java K-means Clustering实现,带有可选的特殊相等选项,在集群上应用相等的基数约束,同时保持尽可能具有空间内聚性。
它还是实验性的,所以请注意known bugs。
答案 16 :(得分:-1)
您想要了解空间填充曲线,例如z曲线或希尔伯特曲线。我想到了一个空间填充曲线,可以将二维问题简化为一维问题。虽然sfc索引只是二维数据的重新排序而不是数据的完美聚类,但是当解决方案不满足完美的集群并且必须相当快地计算时,它可能是有用的。