具有多个布尔数组的 Numpy 多维切片
Numpy multi-dimensional slicing with multiple boolean arrays
我正在尝试使用单个一维布尔数组对多维数组进行切片。出于某种原因,此代码不起作用:
>>> a = np.ones((100, 200, 300, 2))
>>> a.shape
(100, 200, 300, 2)
>>> m1 = np.asarray([True]*200)
>>> m2 = np.asarray([True]*300)
>>> m2[-1] = False
>>> a[:,m1,m2,:]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (200,) (299,)
>>> m2 = np.asarray([True]*300) # try again with all 300 dimensions True
>>> a[:,m1,m2,:]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (200,) (300,)
但这工作得很好:
>>> a = np.asarray([[[1, 2], [3, 4], [5, 6]], [[11, 12], [13, 14], [15, 16]]])
>>> a.shape
(2, 3, 2)
>>> m1 = np.asarray([True, False, True])
>>> m2 = np.asarray([True, False])
>>> a[:,m1,m2]
array([[ 1, 5],
[11, 15]])
知道我在第一个示例中可能做错了什么吗?
简答:m1
和m2
中True元素的数量必须匹配,除非其中一个只有一个True项。
还要区分 'diagonal' 索引和 'rectangular' 索引。这是关于索引,而不是切片。带有 :
的尺寸只是顺带一提。
初步想法
我可以让你的第一个案例与:
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_
将 2 个布尔数组转换为可广播索引数组
In [143]: I.shape
Out[143]: (200, 1)
In [144]: J.shape
Out[144]: (1, 299)
请注意,这会在一个维度中选择 200 'rows',在另一个维度中选择 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)]
# or I,J=np.ix_(n1,n2);b[:,I,J]
Out[164]:
array([[[ 0],
[ 4]],
[[ 6],
[10]]])
In [165]: _.shape
Out[165]: (2, 2, 1)
两种情况都使用第一维的所有行。 ix
选择第二个暗淡的 2 'rows' 和最后一个暗淡的 1 列,得到 (2,2,1) 形状。另一个选择 b[:,0,0]
和 b[0,2,0]
项,得到 (2,2) 形状。
(请参阅我的附录,了解为什么两者都只是广播)。
这些都是高级索引的情况,具有布尔和数字索引。可以研究文档,也可以四处游玩。有时做后者更有趣。 :)
(我知道 ix_
很适合将必要的 np.newaxis
添加到数组中,因此可以一起广播,但没有意识到它也适用于布尔数组 - 它使用 np.nonzero()
将布尔值转换为索引。)
分辨率
我认为,这背后的原因是对 2 种索引模式的混淆。这可能称为 'diagonal' 和 'rectangular'(或 element-by-element 选择与块选择)。为了说明看一个小的二维数组
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); m2=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]])
第1选2分,M[0,0]
和M[1,1]
。这种索引让我们可以挑选出数组的对角线。
第二个选择 2 行和 2 列。这是 np.ix_
产生的索引类型。第 1 选 2 分,M[0,0]
和 M[1,1]
。这是一种 'rectangular' 形式的索引。
将 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
只有一个元素,我们不会得到广播错误 - 因为 size 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 元素。 a[:,m1,m2,:]
由于 True 项的数量不匹配而失败。
在第 2 个中,它们有 2 个和 1 个真项,非零索引 [0,2]
和 [0]
,可以广播到 [0,0]
。所以它运行。
http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html
根据 nonzero
和 ix_
.
解释布尔数组索引
Combining multiple Boolean indexing arrays or a Boolean with an integer indexing array can best be understood with the obj.nonzero() analogy. The function ix_ also supports boolean arrays and will work without any surprises.
附录
进一步思考 'diagonal' 和 'block/rectangular' 索引之间的区别可能更多是我的心理构造 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),)
现在尝试在 'diagonal' 和 'rectangular' 模式下广播:
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)
.
这可能是一个简单的解决方法:
a[:,m1,:,:][:,:,m2,:]
我正在尝试使用单个一维布尔数组对多维数组进行切片。出于某种原因,此代码不起作用:
>>> a = np.ones((100, 200, 300, 2))
>>> a.shape
(100, 200, 300, 2)
>>> m1 = np.asarray([True]*200)
>>> m2 = np.asarray([True]*300)
>>> m2[-1] = False
>>> a[:,m1,m2,:]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (200,) (299,)
>>> m2 = np.asarray([True]*300) # try again with all 300 dimensions True
>>> a[:,m1,m2,:]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: shape mismatch: indexing arrays could not be broadcast together with shapes (200,) (300,)
但这工作得很好:
>>> a = np.asarray([[[1, 2], [3, 4], [5, 6]], [[11, 12], [13, 14], [15, 16]]])
>>> a.shape
(2, 3, 2)
>>> m1 = np.asarray([True, False, True])
>>> m2 = np.asarray([True, False])
>>> a[:,m1,m2]
array([[ 1, 5],
[11, 15]])
知道我在第一个示例中可能做错了什么吗?
简答:m1
和m2
中True元素的数量必须匹配,除非其中一个只有一个True项。
还要区分 'diagonal' 索引和 'rectangular' 索引。这是关于索引,而不是切片。带有 :
的尺寸只是顺带一提。
初步想法
我可以让你的第一个案例与:
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_
将 2 个布尔数组转换为可广播索引数组
In [143]: I.shape
Out[143]: (200, 1)
In [144]: J.shape
Out[144]: (1, 299)
请注意,这会在一个维度中选择 200 'rows',在另一个维度中选择 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)]
# or I,J=np.ix_(n1,n2);b[:,I,J]
Out[164]:
array([[[ 0],
[ 4]],
[[ 6],
[10]]])
In [165]: _.shape
Out[165]: (2, 2, 1)
两种情况都使用第一维的所有行。 ix
选择第二个暗淡的 2 'rows' 和最后一个暗淡的 1 列,得到 (2,2,1) 形状。另一个选择 b[:,0,0]
和 b[0,2,0]
项,得到 (2,2) 形状。
(请参阅我的附录,了解为什么两者都只是广播)。
这些都是高级索引的情况,具有布尔和数字索引。可以研究文档,也可以四处游玩。有时做后者更有趣。 :)
(我知道 ix_
很适合将必要的 np.newaxis
添加到数组中,因此可以一起广播,但没有意识到它也适用于布尔数组 - 它使用 np.nonzero()
将布尔值转换为索引。)
分辨率
我认为,这背后的原因是对 2 种索引模式的混淆。这可能称为 'diagonal' 和 'rectangular'(或 element-by-element 选择与块选择)。为了说明看一个小的二维数组
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); m2=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]])
第1选2分,M[0,0]
和M[1,1]
。这种索引让我们可以挑选出数组的对角线。
第二个选择 2 行和 2 列。这是 np.ix_
产生的索引类型。第 1 选 2 分,M[0,0]
和 M[1,1]
。这是一种 'rectangular' 形式的索引。
将 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
只有一个元素,我们不会得到广播错误 - 因为 size 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 元素。 a[:,m1,m2,:]
由于 True 项的数量不匹配而失败。
在第 2 个中,它们有 2 个和 1 个真项,非零索引 [0,2]
和 [0]
,可以广播到 [0,0]
。所以它运行。
http://docs.scipy.org/doc/numpy-1.10.0/reference/arrays.indexing.html
根据 nonzero
和 ix_
.
Combining multiple Boolean indexing arrays or a Boolean with an integer indexing array can best be understood with the obj.nonzero() analogy. The function ix_ also supports boolean arrays and will work without any surprises.
附录
进一步思考 'diagonal' 和 'block/rectangular' 索引之间的区别可能更多是我的心理构造 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),)
现在尝试在 'diagonal' 和 'rectangular' 模式下广播:
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)
.
这可能是一个简单的解决方法:
a[:,m1,:,:][:,:,m2,:]