我有一个矩阵,表示到一组点的k最近邻居的距离, 并且有一个最近邻居的类标签矩阵。 (均为N-by-k矩阵)
没有显式python循环的最佳方法是什么(实际上,我想在那些循环不起作用的theano中实现它)来构建一个(N-by-#类)矩阵,其(i,j)元素将是从第i个点到其k-NN点的距离之和与类标签'j'?
实施例:
# N = 2
# k = 5
# number of classes = 3
K_val = np.array([[1,2,3,4,6],
[2,4,5,5,7]])
l_val = np.array([[0,1,2,0,1],
[2,0,1,2,0]])
"""
result -> [[5,8,3],
[11,5,7]]
"""
答案 0 :(得分:2)
您可以使用此计算
numpy.bincount。它
有一个weights
参数,可让您计算l_val
中的项目
但是根据K_val
对项目进行加权。
唯一的小障碍是K_val
和l_val
的每一行似乎都是独立对待的。因此,向l_val
添加一个班次,这样每行的值都与其他每一行都不同。
import numpy as np
num_classes = 3
K_val = np.array([[1,2,3,4,6],
[2,4,5,5,7]])
l_val = np.array([[0,1,2,0,1],
[2,0,1,2,0]])
def label_distance(l_val, K_val):
nrows, ncols = l_val.shape
shift = (np.arange(nrows)*num_classes)[:, np.newaxis]
result = (np.bincount((l_val+shift).ravel(), weights=K_val.ravel(),
minlength=num_classes*nrows)
.reshape(nrows, num_classes))
return result
print(label_distance(l_val, K_val))
产量
[[ 5. 8. 3.]
[ 11. 5. 7.]]
尽管senderle的方法非常优雅,但使用bincount更快:
def using_extradim(l_val, K_val):
return (K_val[:,:,None] * (l_val[:,:,None] == numpy.arange(3)[None,None,:])).sum(axis=1)
In [34]: K2 = np.tile(K_val, (1000,1))
In [35]: L2 = np.tile(l_val, (1000,1))
In [36]: %timeit using_extradim(L2, K2)
1000 loops, best of 3: 584 µs per loop
In [40]: %timeit label_distance(L2, K2)
10000 loops, best of 3: 67.7 µs per loop
答案 1 :(得分:2)
这是一种直接计算值的方法。正如unutbu的测试显示的那样,使用bincount
对于大型数据集要快得多,但我认为值得知道如何使用香草广播来做到这一点:
>>> (K_val[:,:,None] * (l_val[:,:,None] == numpy.arange(3)[None,None,:])).sum(axis=1)
array([[ 5, 8, 3],
[11, 5, 7]])
那有点毛茸茸,所以我会慢慢走过去。在您希望以后能够阅读的代码中,最好这样做!有四个步骤:
labels = numpy.arange(3)
l_selector = l_val[:,:,None] == labels[None,None,:]
distances = (K_val[:,:,None] * l_selector)
result = distances.sum(axis=1)
首先,我们创建一个标签列表(上面为labels
)。然后我们创建一个布尔索引数组:
>>> l_selector = l_val[:,:,None] == labels[None,None,:]
这会将l_val
和labels
扩展为可以一起广播的数组。 None
值(相当于np.newaxis
)会添加新的空尺寸:
>>> l_val[:,:,None].shape
(2, 5, 1)
>>> labels[None,None,:].shape
(1, 1, 3)
尺寸是对齐的,因此两个数组都可以沿着它们的空尺寸扩展(通过重复值):
>>> l_selector.shape
(2, 5, 3)
现在我们有一个(n_points, n_neighbors, n_labels)
数组,其中每列对应一个标签。 (看看每行只有一个True
值?)
>>> l_selector
array([[[ True, False, False],
[False, True, False],
[False, False, True],
[ True, False, False],
[False, True, False]],
[[False, False, True],
[ True, False, False],
[False, True, False],
[False, False, True],
[ True, False, False]]], dtype=bool)
现在我们可以使用它来分离三个标签中每个标签的距离。但同样,我们必须确保我们的数组是可广播的,因此K_val[:,:,None]
在这里:
>>> distances = (K_val[:,:,None] * l_selector)
>>> distances
array([[[1, 0, 0],
[0, 2, 0],
[0, 0, 3],
[4, 0, 0],
[0, 6, 0]],
[[0, 0, 2],
[4, 0, 0],
[0, 5, 0],
[0, 0, 5],
[7, 0, 0]]])
现在我们所要做的就是对列进行总结。
>>> result = distances.sum(axis=1)
>>> result
array([[ 5, 8, 3],
[11, 5, 7]])
您可能还会考虑转置方法,这需要更少的重塑:
>>> labels = numpy.arange(3)
>>> l_selector = l_val[None,:,:] == labels[:,None,None]
>>> distances = K_val * l_selector
>>> distances.sum(axis=-1)
array([[ 5, 11],
[ 8, 5],
[ 3, 7]])
>>> distances.sum(axis=-1).T
array([[ 5, 8, 3],
[11, 5, 7]])