请考虑以下内容:
import numpy as np
arr = np.arange(3 * 4 * 5).reshape((3, 4, 5))
如果我使用arr
切成slice
,例如:
arr[:, 0:2, :].shape
# (3, 2, 5)
如果现在我使用arr
和slice()
的混合物对tuple()
进行切片,则会得到:
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)]
相同的结果,但使用单个切片?
现在,这个示例并不那么有趣,因为我可以将tuple()
替换为slice()
,但是如果那不是真的,那么所有这些都变得非常重要,例如:
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的评论/答案指向正确广播的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
答案 0 :(得分: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]]]
答案 1 :(得分:0)
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]],
....
许多numpy
函数中都使用了用Python代码构建元组,然后在索引表达式中使用它的想法。
构造该索引元组的另一种方法是:
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
函数调用。