基于条件的Numpy split数组没有for循环

Sha*_*ear 5 python arrays performance numpy vectorization

所以假设我有一个numpy数组,它在2d空间中保存点,如下所示

np.array([[3, 2], [4, 4], [5, 4], [4, 2], [4, 6], [9, 5]]) 
Run Code Online (Sandbox Code Playgroud)

我还有一个numpy数组,它将每个点标记为一个数字,这个数组是一个1d数组,其长度为点数组中的点数.

np.array([0, 1, 1, 0, 2, 1])
Run Code Online (Sandbox Code Playgroud)

现在我想取每个具有来自labels数组的索引的点的平均值.因此,对于标记为0的所有点,取这些点的平均值.我目前解决这个问题的方法是以下方式

return np.array([points[labels==k].mean(axis=0) for k in range(k)])
Run Code Online (Sandbox Code Playgroud)

其中k是标签数组中的最大数字,或者称为标记点的方式的数量.

我想要一种方法来做到这一点,而不使用for循环,也许一些我尚未发现的numpy功能?

Div*_*kar 4

方法#1:我们可以利用matrix-multiplication以下方面的帮助braodcasting

mask = labels == np.arange(labels.max()+1)[:,None]
out = mask.dot(points)/np.bincount(labels).astype(float)[:,None]
Run Code Online (Sandbox Code Playgroud)

样本运行 -

In [36]: points = np.array([[3, 2], [4, 4], [5, 4], [4, 2], [4, 6], [9, 5]]) 
    ...: labels = np.array([0, 1, 1, 0, 2, 1])

# Original soln
In [37]: L = labels.max()+1

In [38]: np.array([points[labels==k].mean(axis=0) for k in range(L)])
Out[38]: 
array([[3.5       , 2.        ],
       [6.        , 4.33333333],
       [4.        , 6.        ]])

# Proposed soln
In [39]: mask = labels == np.arange(labels.max()+1)[:,None]
    ...: out = mask.dot(points)/np.bincount(labels).astype(float)[:,None]

In [40]: out
Out[40]: 
array([[3.5       , 2.        ],
       [6.        , 4.33333333],
       [4.        , 6.        ]])
Run Code Online (Sandbox Code Playgroud)

方法#2:使用np.add.at-

sums = np.zeros((labels.max()+1,points.shape[1]),dtype=float)
np.add.at(sums,labels,points)
out = sums/np.bincount(labels).astype(float)[:,None]
Run Code Online (Sandbox Code Playgroud)

方法#3:如果 0 到 max-label 中的序列中的所有数字都出现在 中labels,我们还可以使用np.add.reduceat-

sidx = labels.argsort()
sorted_points = points[sidx]
sums = np.add.reduceat(sorted_points,np.r_[0,np.bincount(labels)[:-1].cumsum()])
out = sums/np.bincount(labels).astype(float)[:,None]
Run Code Online (Sandbox Code Playgroud)