布尔索引行为的解释

Explanation of boolean indexing behaviors

对于二维数组y:

y = np.arange(20).reshape(5,4)
---
[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]
 [12 13 14 15]
 [16 17 18 19]]

所有索引 select 第 1、3 和 5 行。这很清楚。

print(y[
    [0, 2, 4],
    ::
])
print(y[
    [0, 2, 4],
    ::
])
print(y[
    [True, False, True, False, True],
    ::
])
---
[[ 0  1  2  3]
 [ 8  9 10 11]
 [16 17 18 19]]

问题

请帮助理解产生结果的规则或机制。

用元组替换 [] 会生成一个形状为 (0, 5, 4) 的空数组。

y[
    (True, False, True, False, True)
]
---
array([], shape=(0, 5, 4), dtype=int64)

使用单个 True 添加一个新轴。

y[True]
---
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]]])


y[True].shape
---
(1, 5, 4)

添加额外的布尔值 True 产生相同的结果。

y[True, True]
---
array([[[ 0,  1,  2,  3],
        [ 4,  5,  6,  7],
        [ 8,  9, 10, 11],
        [12, 13, 14, 15],
        [16, 17, 18, 19]]])

y[True, True].shape
---
(1, 5, 4)

但是,添加 False 布尔值会再次导致空数组。

y[True, False]
---
array([], shape=(0, 5, 4), dtype=int64)

不确定文档是否解释了此行为。

In general if an index includes a Boolean array, the result will be identical to inserting obj.nonzero() into the same position and using the integer array indexing mechanism described above. x[ind_1, boolean_array, ind_2] is equivalent to x[(ind_1,) + boolean_array.nonzero() + (ind_2,)].

If there is only one Boolean array and no integer indexing array present, this is straight forward. Care must only be taken to make sure that the boolean index has exactly as many dimensions as it is supposed to work with.

布尔标量索引没有详细记录,但您可以在源代码中跟踪它是如何处理的。例如,请参阅 numpy source:

中的评论和相关代码
/*
* This can actually be well defined. A new axis is added,
* but at the same time no axis is "used". So if we have True,
* we add a new axis (a bit like with np.newaxis). If it is
* False, we add a new axis, but this axis has 0 entries.
*/

因此,如果索引是标量布尔值,则会添加一个新轴。如果值为 True 轴的大小为 1,如果值为 False,则轴的大小为零。

此行为是在 numpy#3798, and the author outlines the motivation in this comment 中引入的;粗略地说,目的是提供过滤操作输出的一致性。例如:

x = np.ones((2, 2))
assert x[x > 0].ndim == 1

x = np.ones(2)
assert x[x > 0].ndim == 1

x = np.ones(())
assert x[x > 0].ndim == 1  # scalar boolean here!

有趣的是,第一个之后的任何后续标量布尔值都不会添加额外的维度!从实现的角度来看,这似乎是由于连续的 0D 布尔索引被视为等同于连续的花式索引(即 HAS_0D_BOOL 在某些情况下被视为 HAS_FANCY),因此以相同的方式组合作为奇特的指数。从逻辑的角度来看,这种极端情况的行为似乎不是故意的:例如,我在 numpy#3798.

中找不到任何关于它的讨论

鉴于此,我建议考虑这种定义不明确的行为,并避免使用它以支持有据可查的索引方法。