Python NumPy索引:使用布尔数组进行广播

Python NumPy索引:使用布尔数组进行广播,python,arrays,numpy,indexing,array-broadcasting,Python,Arrays,Numpy,Indexing,Array Broadcasting,与之相关的是,我通过布尔数组和广播遇到了一种索引行为,我不理解这种行为。我们知道可以使用整数索引和广播在二维中索引NumPy数组。这在以下文件中有详细说明: 但是,这对于布尔数组不起作用 a[b1[:, None], b2] IndexError: too many indices for array 另一种方法适用于整数数组和布尔数组。这似乎是因为ix对布尔数组执行特定的操作,以确保一致的处理 assert np.array_equal(a[np.ix_(b1, b2)], a[np.ix

与之相关的是,我通过布尔数组和广播遇到了一种索引行为,我不理解这种行为。我们知道可以使用整数索引和广播在二维中索引NumPy数组。这在以下文件中有详细说明:

但是,这对于布尔数组不起作用

a[b1[:, None], b2]

IndexError: too many indices for array
另一种方法适用于整数数组和布尔数组。这似乎是因为
ix
对布尔数组执行特定的操作,以确保一致的处理

assert np.array_equal(a[np.ix_(b1, b2)], a[np.ix_(c1, c2)])

array([[ 4,  6],
       [ 8, 10]])
所以我的问题是:为什么广播工作于整数,而不是布尔数组?这种行为有记录吗?还是我误解了一个更基本的问题?

因为,布尔高级索引的行为就好像它们首先通过
np.非零
输入,然后一起广播一样。引用这些文件

通常,如果索引包含布尔数组,则结果将与将
obj.nonzero()
插入相同位置并使用上述整数数组索引机制相同
x[ind\u 1,boolean\u array,ind\u 2]
相当于
x[(ind\u 1,)+boolean\u array.nonzero()+(ind\u 2,)]

[…]
将多个布尔索引数组或布尔值与整数索引数组组合在一起,最好用
obj.nonzero()
类比来理解。函数
ix_uz
也支持布尔数组,可以正常工作

在您的例子中,广播不一定是一个问题,因为两个数组都只有两个非零元素。问题在于结果中的维度数:

>>> len(b1[:,None].nonzero())
2
>>> len(b2.nonzero())
1
因此,索引表达式
a[b1[:,None],b2]
将等效于
a[b1[:,None].nonzero()+b2.nonzero()]
,这将在
a
中放置一个长度为3的元组,对应于三维数组索引。因此,您会看到关于“索引过多”的错误

文档中提到的惊喜与您的示例非常接近:如果您没有注入单例维度呢?从长度为3和长度为4的布尔数组开始,您将得到长度为2的高级索引,即大小为
(2,)
的1d数组。这绝对不是你想要的,这让我们想到了另一件琐事

在计划改进高级索引方面进行了大量讨论,请参见“正在进行的工作”草稿。问题的要点是,numpy中的奇特索引虽然有明确的文档记录,但有一些非常奇怪的功能,这些功能实际上对任何事情都没有用处,但是如果你犯了错误,产生了令人惊讶的结果而不是错误,这些功能会对你造成伤害

NEP的相关引用:

涉及多个数组索引的混合情况也令人惊讶,而且 问题更少,因为当前行为是如此无用,以至于 在实践中很少遇到这种情况。当布尔数组索引为 与另一个布尔或整数数组混合,布尔数组为 转换为整数数组索引(相当于
np.nonzero()
)和 然后广播。例如,索引大小为
(2,2)
的二维数组,如
x[[True,False],[True,False]]
生成具有形状的一维向量
(1,)
, 不是具有形状
(1,1)
的二维子矩阵

现在,我要强调的是,NEP正在进行大量的工作,但NEP当前状态下的一个建议是禁止在上述高级索引情况下使用布尔数组,并且只允许在“外部索引”情况下使用它们,即
np.ix
将帮助您使用布尔数组:

布尔索引在概念上是外部索引。以传统索引方式(即当前行为)与其他高级索引一起广播通常没有帮助或定义不明确。因此,希望“非零”加广播行为的用户可以手动执行此操作


我的观点是,布尔高级索引的行为及其弃用状态(或缺少)在不久的将来可能会发生变化。

AFAIK在内部,这些布尔数组在用于索引之前会转换为整数等价数组:
np.flatnonzero(ar)
。因此,
np.flatnonzero(b1[:,np.newaxis])
不会等同于
np.flatnonzero(b1[:,None]
。我认为更多的是
np.nonzero(b1[:,None])
np.nonzero(b2)
不能相互广播w.r.t.其各自元组的每个元素,而不是直接输入整数数组,用于索引可通过
np.ix(c1,c2)
广播。感谢接受,但@Divakar是回答您实际问题的人,我真的只是想增加额外的视角。我不愿意相信他的功劳,如果你不接受我的回答,我会更舒服。@AndrasDeak认为谁回答这个问题并不重要,只要它回答的问题令OP满意。如果是这样,我会鼓励OP接受。@Divakar我一直努力做到公平。在这种情况下,谢谢,我将使用您的解决方案进行编辑,使其成为一个完整的答案(下次等待,直到您自己发布答案;)
>>> len(b1[:,None].nonzero())
2
>>> len(b2.nonzero())
1