我可以从 numpy 数组的最后一个维度 select 任意 windows 吗?

Can I select arbitrary windows from the last dimension of a numpy array?

我想编写一个 numpy 函数,它接受一个 MxN 数组 A、一个 window 长度 L 和一个起始索引的 MxP 数组 idxs进入 AM 行,从 A 的 M 行中的每一行中选择 P 个长度为 L 的任意切片。除了,我希望它能在 A 的最后一个维度上工作,而不一定关心 A 有多少维度,所以 Aidxs 的所有维度都匹配除了最后一个。示例:

如果 A 只是一维:

A = np.array([1, 2, 3, 4, 5, 6])

window_len = 3

idxs = np.array([1, 3])

result = magical_routine(A, idxs, window_len)

其中 result 是一个 2x3 数组,因为我选择了 len 3 的 2 个切片:

np.array([[ 2, 3, 4],
          [ 4, 5, 6]])

如果 A 是二维的:

A = np.array([[ 1, 2, 3, 4, 5, 6],
              [ 7, 8, 9,10,11,12],
              [13,14,15,16,17,18]])

window_len = 3
idxs = np.array([[1, 3],
                 [0, 1],
                 [2, 2]])

result = magical_routine(A, idxs, window_len)

其中 result 是一个 3x2x3 数组,因为有 3 行 A,我从每行中选择了 2 片 len 3:

np.array([[[ 2, 3, 4], [ 4, 5, 6]],
          [[ 7, 8, 9], [ 8, 9,10]],
          [[15,16,17], [15,16,17]]])

以此类推

我发现了很多执行此操作的低效方法,以及适用于 A 特定维数的方法。对于 2D,以下内容非常整洁:

col_idxs = np.add.outer(idxs, np.arange(window_len))
np.take_along_axis(A[:, np.newaxis], col_idxs, axis=-1)

不过我看不出有什么好的方法可以将其推广到 1D 和其他 D...

有没有人知道一种推广到任意数量暗淡的有效方法?

对于您的第一个案例

In [271]: A=np.arange(1,7)
In [272]: idxs = np.array([1,3])

使用这个问题通常得到的迭代类型:

In [273]: np.vstack([A[i:i+3] for i in idxs])
Out[273]: 
array([[2, 3, 4],
       [4, 5, 6]])

或者生成所有索引和一个索引。 linspace 对此很方便(虽然它不是唯一的选择):

In [278]: j = np.linspace(idxs,idxs+3,3,endpoint=False)
In [279]: j
Out[279]: 
array([[1., 3.],
       [2., 4.],
       [3., 5.]])
In [282]: A[j.T.astype(int)]
Out[282]: 
array([[2, 3, 4],
       [4, 5, 6]])

为 2d

In [284]: B
Out[284]: 
array([[ 1,  2,  3,  4,  5,  6],
       [ 7,  8,  9, 10, 11, 12],
       [13, 14, 15, 16, 17, 18]])

In [285]: idxs = np.array([[1, 3],
     ...:                  [0, 1],
     ...:                  [2, 2]])

In [286]: j = np.linspace(idxs,idxs+3,3,endpoint=False)

In [287]: j
Out[287]: 
array([[[1., 3.],
        [0., 1.],
        [2., 2.]],

       [[2., 4.],
        [1., 2.],
        [3., 3.]],

       [[3., 5.],
        [2., 3.],
        [4., 4.]]])

经过一些反复试验,配对指数得到:

In [292]: B[np.arange(3)[:,None,None],j.astype(int).transpose(1,2,0)]
Out[292]: 
array([[[ 2,  3,  4],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [ 8,  9, 10]],

       [[15, 16, 17],
        [15, 16, 17]]])

或者像第一种情况一样迭代,但是多了一层:

In [294]: np.array([[B[j,i:i+3] for i in idxs[j]] for j in range(3)])
Out[294]: 
array([[[ 2,  3,  4],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [ 8,  9, 10]],

       [[15, 16, 17],
        [15, 16, 17]]])

滑动windows:

In [295]: aa = np.lib.stride_tricks.sliding_window_view(A,3)

In [296]: aa.shape
Out[296]: (4, 3)

In [297]: aa
Out[297]: 
array([[1, 2, 3],
       [2, 3, 4],
       [3, 4, 5],
       [4, 5, 6]])

In [298]: aa[[1,3]]
Out[298]: 
array([[2, 3, 4],
       [4, 5, 6]])

In [300]: bb = np.lib.stride_tricks.sliding_window_view(B,(1,3))

In [301]: bb.shape
Out[301]: (3, 4, 1, 3)

In [302]: bb[np.arange(3)[:,None],idxs,0,:]
Out[302]: 
array([[[ 2,  3,  4],
        [ 4,  5,  6]],

       [[ 7,  8,  9],
        [ 8,  9, 10]],

       [[15, 16, 17],
        [15, 16, 17]]])

我明白了!我快到了:

def magical_routine(A, idxs, window_len=2000):
    col_idxs = np.add.outer(idxs, np.arange(window_len))
    return np.take_along_axis(A[..., np.newaxis, :], col_idxs, axis=-1)

我只需要始终将新轴添加到 A 的倒数第二个暗淡的位置,然后不理会其余轴。