具有多个维度的Numpy布尔索引。为什么不选择行和列?

时间:2014-10-08 14:37:57

标签: arrays numpy multidimensional-array indexing boolean

我的ndarray尺寸为n>1。我有一个布尔数组ok0对应于我想要选择的行,另一个布尔数组ok1对应于我想要选择的列。我想要包括所有“页面”。所以我尝试Z[ok0, ok1, :],其中ok0是带有ok0.size == Z.shape[0]的1-D布尔数组,ok1是带ok1.size == Z.shape[1]的布尔数组。有没有办法直接使用这些布尔数组索引我的nd-array?

代码片段描绘了千言万语。

In [50]: Z = arange(7*8*9).reshape(7, 8, 9)

In [51]: ok0 = Z.sum(1).sum(1)%10<3

In [52]: ok1 = Z.sum(0).sum(1)%10<5

In [53]: ok0.shape
Out[53]: (7,)

In [54]: ok1.shape
Out[54]: (8,)

In [55]: Z[ok0, :, :].shape
Out[55]: (3, 8, 9)

In [56]: Z[:, ok1, :].shape
Out[56]: (7, 4, 9)

In [57]: Z[ok0, ok1, :].shape
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-57-ebba5b9a19dd> in <module>()
----> 1 Z[ok0, ok1, :].shape

ValueError: shape mismatch: objects cannot be broadcast to a single shape

可以间接实现所需的效果,如下所示:

In [58]: Z[ok0, :, :][:, ok1, :].shape
Out[58]: (3, 4, 9)

如果我将ok0ok1从布尔数组转换为整数数组,我可以使用this answer to Selecting specific rows and columns from NumPy array中提供的解决方案:

In [88]: ok0i = ok0.nonzero()[0]

In [89]: ok1i = ok1.nonzero()[0]

In [90]: Z[ok0i[:, newaxis], ok1i, :].shape
Out[90]: (3, 4, 9)

但是,这不适用于原始布尔数组:

In [87]: Z[ok0[:, newaxis], ok1, :].shape
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-87-7e9fa28c47fa> in <module>()
----> 1 Z[ok0[:, newaxis], ok1, :].shape

ValueError: shape mismatch: objects cannot be broadcast to a single shape

为什么这不起作用 - 这里出了什么问题?并且(如何)我可以一次性实现所需的效果,而不像我在命令58中那样重复我的完整索引(可能很长)?

2 个答案:

答案 0 :(得分:0)

由于您希望根据某些条件选择整行和列,我认为np.take可能是解决此问题的合适解决方案,而无需将现有方法更改为确定所需的行和列,{ {1}}和ok0

ok1

这将首先选择result = np.take(np.take(Z, np.where(ok0)[0], axis=0), np.where(ok1)[0], axis=1) 所在的所有行(axis=0),并从该子集中选择ok0==True所有列(axis=1)。在ok1==True np.where之后需要[0]输出包含索引的数组元组(数组([]),但是你只需要{{1的索引数组}}

此方法的另一个优点是np.where也比使用ndarrays的“花式”索引更有效。

答案 1 :(得分:0)

解决方案

Re:在一个阶段做掩码选择:

In [152]: result = Z[ok0[:, np.newaxis] & ok1].reshape(ok0.sum(), ok1.sum(),
                                                       *Z.shape[2:])

In [153]: result.shape
Out[153]: (3, 4, 9)

In [154]: (result == Z[ok0][:, ok1]).all()
Out[154]: True

Re:长索引:您可以省略任意数量的尾随维度 用省略号(...)替换任意数量的前导维度 完整地指定所有最后的尺寸。

In [155]: Z[0]
Out[155]: 
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8],
       [ 9, 10, 11, 12, 13, 14, 15, 16, 17],
       [18, 19, 20, 21, 22, 23, 24, 25, 26],
       [27, 28, 29, 30, 31, 32, 33, 34, 35],
       [36, 37, 38, 39, 40, 41, 42, 43, 44],
       [45, 46, 47, 48, 49, 50, 51, 52, 53],
       [54, 55, 56, 57, 58, 59, 60, 61, 62],
       [63, 64, 65, 66, 67, 68, 69, 70, 71]])

In [156]: Z[...,0]
Out[156]: 
array([[  0,   9,  18,  27,  36,  45,  54,  63],
       [ 72,  81,  90,  99, 108, 117, 126, 135],
       [144, 153, 162, 171, 180, 189, 198, 207],
       [216, 225, 234, 243, 252, 261, 270, 279],
       [288, 297, 306, 315, 324, 333, 342, 351],
       [360, 369, 378, 387, 396, 405, 414, 423],
       [432, 441, 450, 459, 468, 477, 486, 495]])

描述

掩码选择有效,因为我们可以使用高深度布尔掩码来获取元素 符合条件:

In [157]: arr
Out[157]: 
array([[0, 1, 2],
       [3, 4, 5],
       [6, 7, 8]])

In [158]: (arr % 2 == 0).astype(int)
Out[158]: 
array([[1, 0, 1],
       [0, 1, 0],
       [1, 0, 1]])

In [159]: arr[arr % 2 == 0]
Out[159]: array([0, 2, 4, 6, 8])

可以使用您已使用的广播技巧生成掩码:

In [160]: ok0 = arr.sum(1)%10<3

In [161]: ok1 = arr.sum(0)%10<5

In [162]: (ok0[:, np.newaxis] & ok1).astype(int)
Out[162]: 
array([[0, 0, 0],
       [0, 1, 0],
       [0, 1, 0]])

In [163]: arr[ok0[:, np.newaxis] & ok1]
Out[163]: array([4, 7])

但是你可以注意到这些元素是狂暴的,每个面具都要计算 恢复形状:

In [164]: arr[ok0[:, np.newaxis] & ok1].reshape(ok0.sum(), ok1.sum())
Out[164]: 
array([[4],
       [7]])