使用布尔矩阵替换for循环以执行高级索引

时间:2017-09-11 22:46:12

标签: python arrays numpy matrix indexing

处理三维矩阵时," M"对于尺寸(A,B,C),可以使用2个向量X来索引M,其中元素在[0,A)中,Y在[0,B]中具有相同维度D的元素。

更具体地说,我明白在写作时

M[X,Y,:]

我们正在为每个"我"在D,

M[X[i], Y[i], :],

因此最终产生DxC矩阵。

现在假设

X is a numpy array of dim U, same concept as before
this time Y is a matrix UxL, where each row correspond to a Boolean numpy array 
(a mask)

并查看以下代码

for u in U:
    my_matrix[Y[u], X[u], :] += 1  # Y[u] is the mask that selects specific elements of the first dimension

我想在没有for循环的情况下编写相同的代码。这样的东西

np.add.at(my_matrix, (Y, X), 1) # i use numpy.ufunc.at since same elements could occur multiple times in X or Y.

遗憾地返回以下错误

  

IndexError:布尔索引与维度0的索引数组不匹配; dimension是L但相应的布尔维数是1

执行作业

时也可以找到此问题
for u in U:
    a_matrix[u, Y[u], :] = my_matrix[Y[u], X[u], :]

你知道如何以优雅的方式解决这个问题吗?

1 个答案:

答案 0 :(得分:0)

简单地使用通常的nd-array形状的花式索引的简单方法不太适用于您的问题。这就是为什么我这样说:Y有布尔行,告诉你哪些索引沿着第一个维度。因此,Y[0]Y[1]可能会有不同数量的True元素,因此Y行将沿第一维切片不同长度的子数组。换句话说,您的阵列形索引不能转换为矩形子阵列。

但是如果你考虑一下你的索引数组意味着什么,那么就有了出路。 Y行确切地告诉您要修改哪些元素。如果我们将所有索引混淆为大量的1d花式索引,我们可以精确定位我们想要索引的第一维上的每个(x,y)点。

特别是,请考虑以下示例(顺便说一句,你的问题中很少遗漏):

A = np.arange(4*3*2).reshape(4,3,2)
Y = np.array([[True,False,False,True],
              [True,True,True,False],
              [True,False,False,True]])
X = np.array([2,1,2])

A是形状(4,3,2)Y是形状(3,4)(并且第一行和最后一行有意义相同),X是形状(3 ,)`(并且第一个和最后一个元素的目的是相同的)。让我们将布尔索引转换为线性索引的集合:

U,inds = Y.nonzero()
#U: array([0, 0, 1, 1, 1, 2, 2])
#inds: array([0, 3, 0, 1, 2, 0, 3])

如您所见,UTrue中每个Y元素的行索引。这些是给出Y行和X元素之间对应关系的索引。第二个数组inds是沿第一维的实际线性索引(对于给定的行)。

我们差不多完成了,我们所需要的只是将inds的元素与X的相应索引配对作为第二维。这实际上非常简单:我们只需要使用X索引U

总而言之,以下两个是针对同一问题的等效循环和花式索引解决方案:

B = A.copy()
for u in range(X.size):
    A[Y[u],X[u],:] += 1
U,inds = Y.nonzero()
np.add.at(B,(inds,X[U]),1)

A使用循环进行修改,使用B修改np.add.at。我们可以看到两者是平等的:

>>> (A == B).all()
True

如果你看看这个例子,你可以看到我有意复制了第一和第三组索引。这表明np.add.at正在使用这些花哨的索引,并且累积了在输入上多次出现的索引。 (打印B并与A的初始值进行比较,您可以看到最后一项递增两次。)