布尔索引行为的解释

9

针对二维数组 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]]

所有索引选择第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)

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

一般来说,如果一个索引包含一个布尔数组,则结果将等同于在相同位置插入 obj.nonzero() 并使用上述整数数组索引机制。x[ind_1, boolean_array, ind_2] 等同于 x[(ind_1,) + boolean_array.nonzero() + (ind_2,)]。

如果只有一个布尔数组而没有整数索引数组,那么这就很简单。只需要确保布尔索引的维数与应该使用的维数完全相同即可。


2
我认为这是布尔标量索引(而不是布尔数组/列表索引)。我不记得看到过它的文档。看起来像是True会添加一个前导大小为1的维度,而任何False都会添加一个前导大小为0的维度。我不会指望它在任何生产工作中使用(就好像它是all(the_boolean_tuple)一样)。 - hpaulj
1个回答

4

布尔标量索引并没有得到很好的文档支持,但是你可以在源代码中跟踪它的处理方式。例如,查看NumPy源代码中的这个注释和相关的代码:

/*
* 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中引入的,作者在评论中概述了其动机;大致上,目的是提供过滤操作输出的一致性。例如:
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中我找不到任何讨论它的内容。

考虑到这一点,我建议将此行为视为定义不明确,而优先选择有文档记录的索引方法。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接