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