与this question有关,我通过布尔数组和我不了解的广播遇到了索引行为。我们知道可以使用整数索引和广播在2维中为NumPy数组建立索引。在docs中指定:
a = np.array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
b1 = np.array([False, True, True])
b2 = np.array([True, False, True, False])
c1 = np.where(b1)[0] # i.e. [1, 2]
c2 = np.where(b2)[0] # i.e. [0, 2]
a[c1[:, np.newaxis], c2] # or a[c1[:, None], c2]
array([[ 4, 6],
[ 8, 10]])
但是,对于布尔数组而言,这是行不通的。
a[b1[:, None], b2]
IndexError: too many indices for array
备用numpy.ix_
适用于整数和布尔数组。这似乎是因为ix_
对布尔数组执行特定的操作以确保一致的处理。
assert np.array_equal(a[np.ix_(b1, b2)], a[np.ix_(c1, c2)])
array([[ 4, 6],
[ 8, 10]])
所以我的问题是:为什么广播只能使用整数,而不能使用布尔数组?是否记录了这种行为?还是我误解了一个更根本的问题?
答案 0 :(得分:4)
与@Divakar noted in comments一样,布尔高级索引的行为就像是先通过np.nonzero
馈送然后see the relevant documentation for extensive explanations一起广播一样。要引用文档,
通常,如果索引包含布尔数组,则结果将与将
obj.nonzero()
插入相同位置并使用上述整数数组索引机制相同。x[ind_1, boolean_array, ind_2]
等同于x[(ind_1,) + boolean_array.nonzero() + (ind_2,)]
。
[...]
与obj.nonzero()
类比可以最好地理解将多个布尔值索引数组或布尔值与整数索引数组组合在一起。函数ix_
还支持布尔数组,并且可以正常工作。
在您的情况下,广播不一定是问题,因为两个数组只有两个非零元素。问题是结果中的维数:
>>> len(b1[:,None].nonzero())
2
>>> len(b2.nonzero())
1
因此,索引表达式a[b1[:,None], b2]
等效于a[b1[:,None].nonzero() + b2.nonzero()]
,它将在a
中放入一个长度为3的元组,对应于3d数组索引。因此,您会看到有关“索引过多”的错误。
文档中提到的惊喜与您的示例非常接近:如果您未注入该单例维度该怎么办?从长度为3和长度为4的布尔数组开始,您将得到长度为2的高级索引,即大小为(2,)
的一维数组。这从来都不是您想要的,这将导致我们在该主题中遇到另一个琐事。
在计划改进高级索引方面进行了很多讨论,请参阅进行中的草案NEP 21。问题的要点是,尽管已明确记录,但numpy中的花式索引具有一些非常古怪的功能,这些功能实际上对任何事情都没有用,但是如果您通过产生令人惊讶的结果而不是错误而犯了错误,那么它们可能会咬住您。 >
NEP的相关报价:
涉及多个数组索引的混合案例也令人惊讶,并且 问题更少,因为当前的行为是如此无用,以至于 在实践中很少遇到。当布尔数组索引为 与另一个布尔或整数数组混合,布尔数组是 转换为整数数组索引(相当于
np.nonzero()
),然后 然后播放。例如,索引大小为(2, 2)
的2D数组,例如x[[True, False], [True, False]]
产生形状为(1,)
的一维矢量, 不是形状为(1, 1)
的2D子矩阵。
现在,我强调NEP正在进行中,但是NEP当前状态的建议之一是在上述高级索引情况下禁止布尔数组,并且仅允许将它们用于“外部索引”方案,即确切地说np.ix_
将如何帮助您处理布尔数组:
布尔索引在概念上是外部索引。以传统索引的方式与其他高级索引一起广播[当前的行为]通常没有帮助或定义不明确。因此,希望“非零”加广播行为的用户可以手动执行此操作。
我的观点是,布尔高级索引的行为及其不赞成使用的状态(或不存在)可能在不远的将来发生改变。