创建 numpy 数组的非平凡视图

Creating a non trivial view of numpy array

长话短说:

我正在寻找一种方法来获得 numpy 的非平凡视图,尤其是非连续视图 ndarray

例如,给定一个 1D ndarray x = np.array([1, 2, 3, 4]),有没有办法获得它的非平凡 view,例如np.array([2, 4, 3, 1])?

加长版

问题的背景如下:我有一个形状为 (U, V, S, T) 的 4D ndarray,我想以一种非平凡的方式将其重塑为形状为 (U*S, V*T) 的 2D ndarray,也就是说,一个简单的 np.reshape() 并不能解决问题,因为我有一个更复杂的索引方案,其中重塑的数组在内存中不会连续。在我的例子中,数组相当大,我想获得一个视图而不是数组的副本。

例子

给定一个 x(u, v, s, t) 形状 (2, 2, 2, 2) 的数组:

x = np.array([[[[1, 1], [1, 1]],[[2, 2], [2, 2]]],
              [[[3, 3], [3, 3]], [[4, 4], [4, 4]]]])

我想获取数组的视图 z(a, b)

np.array([[1, 1, 2, 2],
          [1, 1, 2, 2],
          [3, 3, 4, 4],
          [3, 3, 4, 4]])

这对应于 a = u * S + sb = v * T + t 的索引方案,在这种情况下 S = 2 = T.

我试过的

  1. 使用 np.reshape 甚至 as_strided 的各种方法。进行标准的重塑不会改变元素在内存中出现的顺序。我试着玩弄 order='F' 并转置了一下,但不知道哪个给了我正确的结果。

  2. 因为我知道索引方案,所以我尝试使用np.ravel()对数组的扁平视图进行操作。我的想法是根据所需的索引方案创建一个索引数组,并将其应用于展平的数组视图,但不幸的是,fancy/advanced 索引给出了数组的副本,而不是视图。

问题

有什么方法可以实现我正在寻找的索引视图吗?

原则上,我认为这应该是可能的,例如 ndarray.sort() 执行数组的就地非平凡索引。另一方面,这可能是在 C/C++ 中实现的,所以在纯 Python?

中甚至不可能实现

让我们回顾一下数组的基础知识 - 它有一个平面数据缓冲区、一个 shapestridesdtype。这三个属性用于以特定方式 view 数据缓冲区的元素,无论它是简单的 1d 序列、2d 还是更高维度。

真正的 view 比使用相同的数据缓冲区,但对其应用不同的形状、步幅或 dtype。

要从 [1,2,3,4] 得到 [2, 4, 3, 1] 需要从 2 开始,向前跳 2,然后跳回到 1 和向前跳 2。这不是可以表示的常规模式strides.

arr[1::2] 给出 [2,4],arr[0::2] 给出 [1,3].

(U, V, S, T)(U*S, V*T) 需要转置到 (U, S, V, T),然后再整形

arr.transpose(0,2,1,3).reshape(U*S, V*T)

那将需要一份副本,没有办法。

In [227]: arr = np.arange(2*3*4*5).reshape(2,3,4,5)
In [230]: arr1 = arr.transpose(0,2,1,3).reshape(2*4, 3*5)
In [231]: arr1.shape
Out[231]: (8, 15)
In [232]: arr1
Out[232]: 
array([[  0,   1,   2,   3,   4,  20,  21,  22,  23,  24,  40,  41,  42,
         43,  44],
       [  5,   6,   7,   8,   9,  25,  26,  27,  28,  29,  45,  46,  47,
         48,  49],
       ....)

或者用你的x

In [234]: x1 = x.transpose(0,2,1,3).reshape(4,4)
In [235]: x1
Out[235]: 
array([[1, 1, 2, 2],
       [1, 1, 2, 2],
       [3, 3, 4, 4],
       [3, 3, 4, 4]])

请注意元素的顺序不同:

In [254]: x.ravel()
Out[254]: array([1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4])
In [255]: x1.ravel()
Out[255]: array([1, 1, 2, 2, 1, 1, 2, 2, 3, 3, 4, 4, 3, 3, 4, 4])

ndarray.sort 就地改变了数据缓冲区中字节的顺序。它在我们无法访问的低级别运行。它不是原始数组的 view