【问题标题】:NumPy Slicing with Multiple Tuples使用多个元组进行 NumPy 切片
【发布时间】:2019-10-13 04:06:30
【问题描述】:

考虑以下几点:

import numpy as np

arr = np.arange(3 * 4 * 5).reshape((3, 4, 5))

如果我使用slices 切片arr,我会得到,例如:

arr[:, 0:2, :].shape
# (3, 2, 5)

如果现在我使用slice()tuple() 的混合物对arr 进行切片,我得到:

arr[:, (0, 1), :].shape
# (3, 2, 5)

np.all(arr[:, (0, 1), :] == arr[:, :2, :])
# True

和:

arr[:, :, (0, 1)].shape
# (3, 4, 2)

np.all(arr[:, :, (0, 1)] == arr[:, :, :2])
# True

但是,如果我这样做:

arr[:, (0, 1), (0, 1)].shape
# (3, 2)

基本上,arr[:, 0, 0]arr[:, 1, 1] 串联。

我期待得到:

arr[:, (0, 1), (0, 1)].shape
# (3, 2, 2)

np.all(arr[:, (0, 1), (0, 1)] == arr[:, :2, :2])
# True

但显然不是这样的。

如果我连接两个单独的切片,我将能够获得所需的结果,即:

arr[:, (0, 1), :][:, :, (0, 1)].shape
# (3, 2, 2)

np.all(arr[:, (0, 1), :][:, :, (0, 1)] == arr[:, :2, :2])
# True

是否有可能获得与arr[:, (0, 1), :][:, :, (0, 1)] 相同的结果,但使用单个切片

现在,这个例子没那么有趣了,因为我可以用 slice() 替换 tuple(),但如果不是这样,这一切都会变得更加相关,例如:

arr[:, (0, 2, 3), :][:, :, (0, 2, 3, 4)]
# [[[ 0  2  3  4]
#   [10 12 13 14]
#   [15 17 18 19]]

#  [[20 22 23 24]
#   [30 32 33 34]
#   [35 37 38 39]]

#  [[40 42 43 44]
#   [50 52 53 54]
#   [55 57 58 59]]]

arr[:, (0, 2, 3), (0, 2, 3, 4)] 的语法会更方便。

编辑

@Divakar @hpaulj 和 @MadPhysicist cmets / answers 指向正确广播的 Iterable 等效于多个连接切片。

但是,情况并非如此,例如:

s = np.ix_((0, 1), (0, 1, 2, 3))
arr[s[0], slice(3), s[1]]
# [[[ 0  5 10]
#   [ 1  6 11]
#   [ 2  7 12]
#   [ 3  8 13]]
# 
#  [[20 25 30]
#   [21 26 31]
#   [22 27 32]
#   [23 28 33]]]

但是:

arr[(0, 1), :, :][:, :3, :][:, :, (0, 1, 2, 3)]
# [[[ 0  1  2  3]
#   [ 5  6  7  8]
#   [10 11 12 13]]
# 
#  [[20 21 22 23]
#   [25 26 27 28]
#   [30 31 32 33]]]

和:

np.all(arr[:2, :3, :4] == arr[(0, 1), :, :][:, :3, :][:, :, (0, 1, 2, 3)])
# True

np.all(arr[s[0], slice(3), s[1]] == arr[(0, 1), :, :][:, :3, :][:, :, (0, 1, 2, 3)])
# False

【问题讨论】:

  • np.ix_ - x,y = np.ix_([0,2,3],[0,2,3,4]); arr[:,x,y]。或arr[:,np.array([0,2,3])[:,None],[0,2,3,4]].
  • 您期待 MATLAB 风格的索引块。但是要获取元素,例如 MATLAB 中的对角线,您必须使用 sub2ind 将下标转换为平面索引。实际上,在 MATLAB 中简单的事情在 numpy 中(只是)稍微复杂一点,反之亦然。
  • @hpaulj 这与多个串联切片的工作方式不同,而且我看不到让它在单个切片中工作的方法。
  • 将切片放在中间会引发 doxs 中描述的维度顺序问题。 stackoverflow.com/q/55829631/901925
  • 我需要对此进行测试以确定,但我认为 slice 可以被 ix_ 参数中的另一个元组或 arange 替换。

标签: python numpy array-broadcasting numpy-slicing


【解决方案1】:

如果您想以编程方式对数组进行切片,答案是slice 对象,而不是索引序列。比如:2变成slice(None, 2)

np.all(arr[:, slice(None, 2), slice(None, 2)] == arr[:, :2, :2])

切片选择轴的一部分。索引数组不会:它们选择单个元素。在这种情况下,索引的形状决定了输出的形状。

如果要跨多个维度选择任意索引,索引数组的形状必须与所需输出的形状相同,或者向其广播:

arr[:, [[0], [2], [3]], [[0, 2, 3, 4]]]

【讨论】:

  • :2 -> slice(None, 2) 与问题无关。就相同形状的广播而言,NumPy 是否支持一种程序化的获取方式,例如slice(None), (0, 2, 3), (0, 2, 3, 4)slice(None), [[0], [2], [3]], [[0, 2, 3, 4]]]np.ix_() 成功了一半,但不能与 slice() 混合使用。
  • @norok。并非完全不相关。希望我已经澄清了为什么我包含了这一点。
  • 但是这似乎通常不起作用,请参阅更新后的问题。
【解决方案2】:

ix_ 可以通过元组连接与切片组合:

In [568]: arr[(slice(None),)+np.ix_((0,2,3),(0,2,3,4))]                                               
Out[568]: 
array([[[ 0,  2,  3,  4],
        [10, 12, 13, 14],
        [15, 17, 18, 19]],

       [[20, 22, 23, 24],
        [30, 32, 33, 34],
        [35, 37, 38, 39]],

       [[40, 42, 43, 44],
        [50, 52, 53, 54],
        [55, 57, 58, 59]]])

ix_ 元组:

In [569]: np.ix_((0,2,3),(0,2,3,4))                                                                   
Out[569]: 
(array([[0],
        [2],
        [3]]), array([[0, 2, 3, 4]]))

元组连接:

In [570]: (slice(None),)+np.ix_((0,2,3),(0,2,3,4))                                                    
Out[570]: 
(slice(None, None, None), array([[0],
        [2],
        [3]]), array([[0, 2, 3, 4]]))
In [571]: arr[_]                                                                                      
Out[571]: 
array([[[ 0,  2,  3,  4],
        [10, 12, 13, 14],
        [15, 17, 18, 19]],
        ....

使用 Python 代码构建元组,然后在索引表达式中使用它的想法被用于许多numpy 函数中。

另一种构造这个索引元组的方法是:

In [581]: arr[(slice(None), *np.ix_((0,2,3),(0,2,3,4)))]                                              
Out[581]: 
array([[[ 0,  2,  3,  4],
        [10, 12, 13, 14],
        [15, 17, 18, 19]],
       ...

这利用了元组中的 Python '*' 解包(但不直接在索引表达式中)。

这实际上是一种执行arr[:,*ix_[...]] 的方式,会产生语法错误。

总之:

  • numpy 在 Python 中运行,因此受其所有语法规则的约束

  • numpy 使“元素”索引变得容易。块索引有点困难,尽管它实际上遵循相同的broadcasting 规则。

  • MATLAB 有自己的语言和语法。它使块索引变得容易,但元素索引(例如获取对角线)更加笨拙,需要额外的 sub2ind 函数调用。

【讨论】:

  • 这适用于这个例子,但不是一般的(见更新的问题)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-11-07
  • 2013-03-22
  • 2019-03-25
  • 1970-01-01
  • 1970-01-01
  • 2020-12-08
  • 2017-06-24
相关资源
最近更新 更多