为了给我自己的问题提供一个介绍性答案,我建议可以使用形状中每个点之间的距离列表作为衡量指标来执行聚类。
让我们创建一些形状:
shapes = np.array([[[1,4],[4,2],[11,2],[14,0]],
[[4,5],[4,2],[11,2],[13,0]],
[[1,3],[4,2],[11,2],[14,1.5]],
[[3,5],[4,2],[10,7],[7,9]],
[[5,5],[4,2],[10,7],[6,6]]])
def random_rotation():
theta = 3 * np.pi * np.random.random()
rotMatrix = numpy.array([[np.cos(theta), -np.sin(theta)],
[np.sin(theta), np.cos(theta)]])
return rotMatrix
new_shapes = []
for s in shapes:
rr = random_rotation()
new_shapes += [[list(rr.dot(p)) + [0] for p in s]]
new_shapes = np.array(new_shapes)
for i, s in enumerate(new_shapes):
plot(s[:,0], s[:,1], color='black')
text(np.mean(s[:,0]), np.mean(s[:,1]), str(i), fontsize=14)
然后我们创建一些辅助功能和创建包含所有为每个形状(darray
)的顶点间距离的阵列。
import itertools as it
def vec_distance(v1, v2):
'''
The distance between two vectors.
'''
diff = v2 - v1
return math.sqrt(sum(diff * diff))
def distances(s):
'''
Compute the distance array for a shape s.
'''
ds = [vec_distance(p1, p2) for p1,p2 in it.combinations(s, r=2)]
return np.array(ds)
# create an array of inter-shape distances for each shape
darray = np.array([distances(s) for s in new_shapes])
使用Pycluster
将它们分为两个群集。
import Pycluster as pc
clust = pc.kcluster(darray,2)
print clust
并看到我们最终在第一个集群中有三个条目,而在另一个集群中有两个条目。
(array([0, 0, 0, 1, 1], dtype=int32), 4.576996142441375, 1)
但是他们对应哪些形状?
import brewer2mpl
dark2 = brewer2mpl.get_map('Dark2', 'qualitative', 4).mpl_colors
for i,(s,c) in enumerate(zip(new_shapes, clust[0])):
plot(s[:,0], s[:,1], color=dark2[c])
text(np.mean(s[:,0]), np.mean(s[:,1]), str(i), fontsize=14)
看起来不错!问题是随着形状变大,距离数组相对于顶点的数量以二次方增长。我发现了一个描述这个问题的presentation,并提出了一些解决方案(比如我认为是降维的一种形式的SVD)来加速它。
我不打算接受我的答案,因为我对任何其他想法或关于如何解决这个简单问题的想法感兴趣。