我有一个1维的numpy数组,其中每个元素 i 的值指向数组中的另一个索引。每个集群中心都有一个唯一的负(整数)值。目标是将每个元素分配给一个集群。
实施例
# Generated/pre-computed elsewhere
a = np.array([6, 8, 1, -1, 0, 3, -2, 4, -3, 10, 5])
因此,聚类中心是元素3,6和8(因为它们具有负值)并且它们分别被标记为聚类-1,-2和-3。所以,自从
a[0] = 6 --> a[6] = -2,
然后a [0]可以指定为-2。同样,因为
a[5] = 3 --> a[3] = -1,
然后a [5]可以指定为-1。遵循此逻辑,然后可以将所有元素分配给集群中心。结果数组将是:
[-2, -3, -3, -1, -2, -1, -2, -2, -3, -1, -1]
我知道如何在纸上实现这一点,但我不知道如何在代码中完成此操作或在numpy中使用矢量化代码。
更新:根据unutbu的回答,我用for循环替换了while循环,以避免无休止的while循环:
a = np.array([6, 8, 1, -1, 0, 3, -2, 4, -3, 10, 5])
for i in range(len(a)):
mask = a >= 0
if not mask.any(): break
a[mask] = a[a[mask]]
答案 0 :(得分:6)
我们不知道先验查找群集中心需要多少“跳数”。因此,我们必须进行一些迭代并检查我们是否已登陆集群中心:
import numpy as np
a = np.array([6, 8, 1, -1, 0, 3, -2, 4, -3, 10, 5])
for i in a:
mask = a>=0
# We can stop when all the values in `a` are negative
if not mask.any(): break
# perform a hop
a[mask] = a[a[mask]]
print(a)
产量
[-2 -3 -3 -1 -2 -1 -2 -2 -3 -1 -1]
要了解正在发生的事情,可以更清楚地看一下a
更简单的值:
a = np.arange(-1, 10)
print(a)
for i in a:
mask = a>=0
# We can stop when all the values in `a` are negative
if not mask.any(): break
# perform a hop
a[mask] = a[a[mask]]
print(a)
打印
[-1 0 1 2 3 4 5 6 7 8 9]
[-1 -1 0 1 2 3 4 5 6 7 8]
[-1 -1 -1 -1 0 1 2 3 4 5 6]
[-1 -1 -1 -1 -1 -1 -1 -1 0 1 2]
[-1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1]
在这里,我们可以清楚地看到每个索引处的值(即,中的一列) 输出)正在减少。您可能希望值减少1,但是 实际上,由于它们,它们在后续迭代中减少了不止一个 以前的啤酒花的影响。
更一般地说,让G
成为跳转到同一群集的索引序列
值。 循环的不变量是G中的值映射到G 中的值。
此外,在每个索引处,每次迭代距离(即跳数)到a
簇值减少(或为零)。这两个事实共同意味着Banach fixed point theorem
算法收敛到一个固定点。
每次迭代时,填充簇值的位置数也是如此
成倍增长。如果在迭代之前有n
个位置填充了群集值,则跳转后会有2n
个位置填充群集值。所以收敛是指数级的。
在OP领先后,我将while True
循环更改为for-loop
。固定点的执行次数少于len(a)
步,但for i in a
就足够了。
请注意,上面的代码假定每个索引都会收敛到一个群集值。如果不是这样,那么原始while True
循环可能永远循环。 for i in a
将结束,但可能不是所有群集值。说明问题的一个简单示例是a = np.array([1,0])
。
有趣的是,“简单示例”np.arange(-1, 10)
也是最糟糕的情况
根据长度为11的数组所需的最大迭代次数。
由于簇值的数量增长为2 ** n,因此循环最多需要log2(len(a))
次迭代。因此,当a
包含无限循环时,我们可以编写一个函数来返回簇值或引发ValueError:
def converge(a):
for i in range(int(np.log2(len(a)))+1):
mask = a>=0
# We can stop when all the values in `a` are negative
if not mask.any(): break
# perform a hop
a[mask] = a[a[mask]]
else:
# for-loop completed without reaching break
mask = a>=0
if mask.any():
raise ValueError('{!r} contains infinite cycles'.format(a))
return a