【问题标题】:NumPy indexing: broadcasting with Boolean arraysNumPy 索引:使用布尔数组广播
【发布时间】:2018-12-13 03:23:27
【问题描述】:

与this question 相关,我通过布尔数组和广播遇到了我不理解的索引行为。我们知道可以使用整数索引和广播在二维中索引 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]])

所以我的问题是:为什么广播适用于整数,而不适用于布尔数组?这种行为是否记录在案?还是我误解了一个更根本的问题?

【问题讨论】:

  • 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 我一直努力做到公平。在这种情况下,谢谢,我将使用您的解决方案进行编辑以使其成为完整的答案(下次等到您自己发布答案;)

标签: python arrays numpy indexing array-broadcasting


【解决方案1】:

作为@Divakarnoted 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) 的二维数组,例如 x[[True, False], [True, False]] 产生一个形状为(1,) 的一维向量, 不是形状为(1, 1) 的二维子矩阵。

现在,我强调 NEP 正在进行中,但 NEP 当前状态的建议之一是在上述高级索引情况下禁止布尔数组,只允许它们在“外部索引”场景,即 np.ix_ 将帮助您处理布尔数组:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-04-03
    • 2018-07-06
    • 2021-09-19
    • 1970-01-01
    • 2013-07-20
    • 1970-01-01
    • 2021-01-06
    • 2020-03-25
    相关资源
    最近更新 更多