简短回答:除非其中一个矩阵只有一个True项,否则m1
和m2
中True元素的数量必须匹配。
还要区分“对角线”索引和“矩形”索引。这是关于索引而不是切片。带有:
的维度只是为了方便。
初始想法
我可以使用以下代码来解决您的第一个情况:
In [137]: a=np.ones((100,200,300,2))
In [138]: m1=np.ones((200,),bool)
In [139]: m2=np.ones((300,),bool)
In [140]: m2[-1]=False
In [141]: I,J=np.ix_(m1,m2)
In [142]: a[:,I,J,:].shape
Out[142]: (100, 200, 299, 2)
np.ix_
将这两个布尔数组转换为可广播的索引数组。
In [143]: I.shape
Out[143]: (200, 1)
In [144]: J.shape
Out[144]: (1, 299)
请注意,这在一个维度中选择了200个“行”,在另一个维度中选择了299个。
我不确定为什么在这种情况下需要重新处理数组,但在第二种情况下不需要。
In [154]: b=np.arange(2*3*2).reshape((2,3,2))
In [155]: n1=np.array([True,False,True])
In [156]: n2=np.array([True,False])
In [157]: b[:,n1,n2]
Out[157]:
array([[ 0, 4], # shape (2,2)
[ 6, 10]])
采用相同的
ix_
策略会产生相同的值,但形状不同:
In [164]: b[np.ix_(np.arange(b.shape[0]),n1,n2)]
Out[164]:
array([[[ 0],
[ 4]],
[[ 6],
[10]]])
In [165]: _.shape
Out[165]: (2, 2, 1)
两种情况都使用第一维的所有行。ix 选择了第二维的2个“行”和最后一个“列”,结果为(2,2,1)形状。另一个选择b [:,0,0]和b [0,2,0]项,结果为(2,2)形状。(请参见我的补充说明,了解这两者是如何进行广播的)。
(我知道ix_很适合添加必要的np.newaxis以便可以一起广播,但我没有意识到它也可以与布尔数组一起使用 - 它使用np.nonzero()将布尔值转换为索引。)
解决方案
我认为这背后的问题是对两种索引模式的混淆,可以称之为“对角线”和“矩形”(或逐元素选择与块选择)。为了说明这一点,请看一个小的二维数组。
In [73]: M=np.arange(6).reshape(2,3)
In [74]: M
Out[74]:
array([[0, 1, 2],
[3, 4, 5]])
和 2 个简单的数字索引
In [75]: m1=np.arange(2)
它们可以有两种用途:
In [76]: M[m1,m2]
Out[76]: array([0, 4])
并且
In [77]: M[m1[:,None],m2]
Out[77]:
array([[0, 1],
[3, 4]])
第一个选取了2个点,即
M [0,0]
和
M [1,1]
。这种索引方式使我们能够挑选出数组的对角线。
第二个选取了2行,并从中选取了2列。这是
np.ix_
所生成的一种索引方式。第一个选取了2个点,即
M [0,0]
和
M [1,1]
。这是一种“矩形”形式的索引。
将
m2
更改为3个值:
In [78]: m2=np.arange(3)
In [79]: M[m1[:,None],m2] # returns a 2x3
Out[79]:
array([[0, 1, 2],
[3, 4, 5]])
In [80]: M[m1,m2] # produces an error
...
ValueError: shape mismatch: objects cannot be broadcast to a single shape
但是,如果m2
只有一个元素,我们就不会收到广播错误 - 因为在广播期间可以扩展大小为1的维度:
In [81]: m2=np.arange(1)
In [82]: M[m1,m2]
Out[82]: array([0, 3])
现在将索引数组更改为布尔型,每个数组的长度分别匹配相应的维度,2和3。
In [91]: m1=np.ones(2,bool); m2=np.ones(3,bool)
In [92]: M[m1,m2]
...
ValueError: shape mismatch: objects cannot be broadcast to a single shape
In [93]: m2[2]=False # m1 and m2 each have 2 True elements
In [94]: M[m1,m2]
Out[94]: array([0, 4])
In [95]: m2[0]=False # m2 has 1 True element
In [96]: M[m1,m2]
Out[96]: array([1, 4])
使用2和3个True项会出现错误,但是使用2和2或2和1项就可以运行 - 就好像我们使用了True元素的索引一样:
np.nonzero(m2)
。
将其应用于您的示例。在第一个示例中,
m1
和
m2
有200和299个True元素。由于True项数量不匹配,
a[:,m1,m2,:]
失败。
在第二个示例中,它们有2和1个True项,非零索引为
[0,2]
和
[0]
,可以广播为
[0,0]
。所以它可以运行。
http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html解释了布尔数组索引与
nonzero
和
ix_
的关系。
将多个布尔索引数组或布尔索引数组与整数索引数组相结合,最好可以通过 obj.nonzero() 类比来理解。函数 ix_ 也支持布尔数组,并且可以在没有任何意外情况下正常工作。
补充
经过进一步思考,“对角线”和“块状/矩形”索引之间的区别可能更多地是我的心理构建,而不是 numpys
。两者的基础都是广播的概念。
取布尔值 n1
和 n2
,并获取它们的 nonzero
等效项:
In [107]: n1
Out[107]: array([ True, False, True], dtype=bool)
In [108]: np.nonzero(n1)
Out[108]: (array([0, 2], dtype=int32),)
In [109]: n2
Out[109]: array([ True, False], dtype=bool)
In [110]: np.nonzero(n2)
Out[110]: (array([0], dtype=int32),)
现在尝试在“对角线”和“矩形”模式下进行广播:
In [105]: np.broadcast_arrays(np.array([0,2]),np.array([0]))
Out[105]: [array([0, 2]),
array([0, 0])]
In [106]: np.broadcast_arrays(np.array([0,2])[:,None],np.array([0]))
Out[106]:
[array([[0],
[2]]),
array([[0],
[0]])]
一个产生(2,)
数组,另一个产生(2,1)
。