【问题标题】:Differences in using range() or slice() to slice a 2-d ndarray使用 range() 或 slice() 对二维 ndarray 进行切片的区别
【发布时间】:2020-06-15 03:58:13
【问题描述】:

当我尝试了解 slice() 的工作原理时,我在使用 range() 与 slice() 时遇到了一些有趣的结果。我不知道如何解释机制。任何帮助将不胜感激。

例如: 给定一个 np 数组:

a = np.array(range(100)).reshape(10,10)

a[slice(0,10,2)]a[range(0,10,2)] 相同。

然而,

a[(slice(0,10,2),slice(0,10,2))] 

[[0,2...],[20,22...],[40,42,44]...]

但是

a[(range(0,10,2),range(0,10,2))] 

[0,22,44,66...]

谁能解释一下?

【问题讨论】:

  • 范围索引和切片索引通常是两个非常不同的东西。您遇到了一个给出相同结果的案例,但请注意,切片版本会创建底层缓冲区的 视图,而使用 range 对象进行索引会创建一个新的底层缓冲区。

标签: python numpy slice numpy-slicing


【解决方案1】:

一般来说,范围索引和切片索引是两个非常不同的东西。您遇到了一个给出相同结果的案例,但请注意,切片版本会创建底层缓冲区的 视图,而使用 range 对象进行索引会创建一个新的底层缓冲区。

请注意:

>>> a = np.array(range(100)).reshape(10,10)
>>> s = a[slice(0,10,2)]
>>> r = a[range(0,10,2)]
>>> a[0,0] = 1000
>>> a
array([[1000,    1,    2,    3,    4,    5,    6,    7,    8,    9],
       [  10,   11,   12,   13,   14,   15,   16,   17,   18,   19],
       [  20,   21,   22,   23,   24,   25,   26,   27,   28,   29],
       [  30,   31,   32,   33,   34,   35,   36,   37,   38,   39],
       [  40,   41,   42,   43,   44,   45,   46,   47,   48,   49],
       [  50,   51,   52,   53,   54,   55,   56,   57,   58,   59],
       [  60,   61,   62,   63,   64,   65,   66,   67,   68,   69],
       [  70,   71,   72,   73,   74,   75,   76,   77,   78,   79],
       [  80,   81,   82,   83,   84,   85,   86,   87,   88,   89],
       [  90,   91,   92,   93,   94,   95,   96,   97,   98,   99]])
>>> s
array([[1000,    1,    2,    3,    4,    5,    6,    7,    8,    9],
       [  20,   21,   22,   23,   24,   25,   26,   27,   28,   29],
       [  40,   41,   42,   43,   44,   45,   46,   47,   48,   49],
       [  60,   61,   62,   63,   64,   65,   66,   67,   68,   69],
       [  80,   81,   82,   83,   84,   85,   86,   87,   88,   89]])
>>> r
array([[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9],
       [20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
       [40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
       [60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
       [80, 81, 82, 83, 84, 85, 86, 87, 88, 89]])

当您使用切片时,您将获得切片语义。范围对象被视为一系列索引。这会触发advanced indexing behavior

所以从文档中:

当索引包含的整数数组数量与所要的数组一样多时 indexed 有维度,索引是直截了当的,但是 不同于切片。

所以,ARR[[x1, x2, ..., xn], [y1, y2, ..., yn]] 会给你类似的东西

[ARR[x1,y1], ARR[x2,y2], ... ARR[xn, yn]]

正如@ShadowRanger 在 cmets 中指出的那样,如果您想要使用范围索引的复制语义,您可能仍然应该使用a[:10:2,:10:2].copy(),因为它会更快。

【讨论】:

  • 注意:即使您在“等效”情况下想要一个新的底层缓冲区,您也应该使用slice(通常通过正常的切片语法隐含,而不是显式的slice 构造函数调用,例如a[slice(0,10,2),slice(0,10,2)] 应该是a[:10:2,:10:2]),然后在结果上调用.copy()。使用 range 必须单独将每个索引转换为 C 级变量,查找该单个值,存储它,然后移动到下一个;它比切片慢得多,切片只能转换切片边界、批量分配,然后使用廉价的 C 级循环完成所有切片工作。
  • @ShadowRanger 是的,完全同意。
  • 旁注,但切线相关:a[(range(0,10,2), range(0,10,2))] 可以变成一个切片,返回一个视图为a.ravel()[:min(10*a.shape[0], 10*a.shape[1]) : 2*a.shape[1]+2]
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-01-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-04-27
  • 1970-01-01
  • 2021-01-29
相关资源
最近更新 更多