Python 通过使用布尔矩阵执行高级索引来替换for循环

Python 通过使用布尔矩阵执行高级索引来替换for循环,python,arrays,numpy,matrix,indexing,Python,Arrays,Numpy,Matrix,Indexing,当处理维度(a,B,C)的三维矩阵“M”时,可以使用两个向量X和Y索引M,其中X的元素在[0,a]中,Y的元素在[0,B]中具有相同维度D 更具体地说,我在写作时理解这一点 M[X,Y,:] 对于D中的每个“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 nu

当处理维度(a,B,C)的三维矩阵“M”时,可以使用两个向量X和Y索引M,其中X的元素在[0,a]中,Y的元素在[0,B]中具有相同维度D

更具体地说,我在写作时理解这一点

M[X,Y,:]
对于D中的每个“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.
不幸的是,它返回以下错误

索引器错误:布尔索引与维度0上的索引数组不匹配;维度为L,但对应的布尔维度为1

执行分配时也会发现此问题

for u in U:
    a_matrix[u, Y[u], :] = my_matrix[Y[u], X[u], :]

你知道我该如何优雅地解决这个问题吗?

简单使用通常的nd数组形状的奇特索引的简单方法不太可能解决你的问题。这就是为什么我这么说:
Y
有布尔行,它们告诉你在第一维度上使用哪些索引。所以
Y[0]
Y[1]
可能具有不同数量的
True
元素,因此
Y
行将沿第一维度以不同长度分割子阵列。换句话说,数组形状的索引无法转换为矩形子阵列

但是如果你想一想你的索引数组意味着什么,就有一条出路。
Y
的行准确地告诉你要修改哪些元素。如果我们把所有的索引整理成一个巨大的1d奇特索引集合,我们可以沿着我们要索引的第一个维度精确地定位每个
(x,Y)

特别是,考虑下面的例子(顺便提一下你的问题):

A
是shape
(4,3,2)
Y
是shape
(3,4)
(第一行和最后一行故意相同),
X
是shape(3,)`(第一行和最后一行故意相同)。让我们将布尔索引转换为线性索引的集合:

U,inds = Y.nonzero()
#U: array([0, 0, 1, 1, 1, 2, 2])
#inds: array([0, 3, 0, 1, 2, 0, 3])
如您所见,
U
Y
中每个
True
元素的行索引。这些索引给出了
Y
行与
X
元素之间的对应关系。第二个数组,
inds
是沿第一维的实际线性索引(对于给定行)

我们几乎完成了,我们只需要将
inds
的元素与
X
中对应的第二维度索引配对。这实际上相当简单:我们只需要用
U
索引
X

总而言之,以下两个是相同问题的等效循环和奇特索引解决方案:

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
的初始值进行比较,您可以看到最后的项目增加了两次。)

您能否给出一个最小的工作示例,可以使用3x3x2矩阵或易于可视化的东西?为了确保我理解,
Y
是一个2D矩阵,
Y[u]
是索引
Y
维度的1D掩码。但是
X
是1D矩阵,因此
X[u]
是单个元素。换句话说,在working for循环中,每次只在
Y
所指示的所有三个维度的行中向单个列添加1。因此
Y[u]
可能表示一些相同的行,这相当于将1多次添加到这些元素中。是否正确?如果是这样,我认为最好的方法是先折叠
Y
向量,然后计算一个求和矩阵来添加到整个元素中。@AlexanderReynolds正确。我用一个进一步的分配问题,
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).all()
True