Numpy沿一个轴提取任意子阵列

时间:2016-04-17 05:52:48

标签: python arrays numpy

例如,我有一个三维数组:

[[[ 0  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]]]

和我想要的最终数组:

[[[ 0  1]
  [ 4  5]]

 [[18 19]
  [22 23]]

 [[26 27]
  [30 31]]]

是否有任何有效的方法可以在不使用for循环的情况下获取类似的数组?

提出这个问题的原因是,如果我们想要单独获取任意元素的一个轴,例如:

[[ 0  1  2  3]
 [ 4  5  6  7]
 [ 8  9 10 11]]

我可以使用[ 2 7 9]获取数组a[np.arange(a.shape[0]), [2, 3, 1]] 所以我想知道当元素成为子阵列时是否存在类似的方式。

4 个答案:

答案 0 :(得分:3)

一个简单的想法是a[[0,1,2],[0:2,1:3,0:2],[0:2,2:4,2:4]],但它没有实现。

可以使用np.lib.stride_tricks.as_strided获得解决方法。只需定义:

ast=np.lib.stride_tricks.as_strided(a,a.shape*2,a.strides*2)
#ast.shape is (3, 3, 4, 3, 3, 4).

然后你可以单独定义集团的起源和大小:

In [4]: ast[[0,1,2],[0,1,0],[0,2,2],0,:2,:2]
Out[4]: 
array([[[ 0,  1],
        [ 4,  5]],

       [[18, 19],
        [22, 23]],

       [[26, 27],
        [30, 31]]])

一些解释:

  • 原产地:

您想要找到以元素0,18,26开头的集团。

重构数组中的索引可以通过以下方式找到:

In [316]: np.unravel_index([0,18,26],a.shape)
Out[316]: 
(array([0, 1, 2], dtype=int64),
 array([0, 1, 0], dtype=int64),
 array([0, 2, 2], dtype=int64))

ast[[0,1,2],[0,1,0],[0,2,2]]是一个(3,3,3,4)数组。每个(3,3,4)数组都以选定的元素开头。

array([[[[          0,           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]]],


       [[[         18,          19,          20,          21],
         [         22,          23,          24,          25],
         [         26,          27,          28,          29]],

        [[         30,          31,          32,          33],
         [         34,          35,    23592960,       18335],
         [  697780028, -2147480064,   540876865,  1630433390]],

        [[ 2036429426,   538970664,   538976288,  1532698656],
         [  741355058,   808334368,   775168044,   874523696],
         [  744304686,   538976266,   538976288,   811278368]]],


       [[[         26,          27,          28,          29],
         [         30,          31,          32,          33],
         [         34,          35,    23592960,       18335]],

        [[  697780028, -2147480064,   540876865,  1630433390],
         [ 2036429426,   538970664,   538976288,  1532698656],
         [  741355058,   808334368,   775168044,   874523696]],

        [[  744304686,   538976266,   538976288,   811278368],
         [  539766830,   741355058,   808333600,   775036972],
         [  170679600,   538976288,   538976288,   774920992]]]])

documentation所述,as_strided是一个危险的黑客,必须小心使用,因为如果使用不当,它可以访问不在数组中的元素。下一步将确保选择有效元素。

  • 尺寸:

有趣的元素是每个第一个集团左上角的四个元素。所以ast[[0,1,2],[0,1,0],[0,2,2],0,:2,:2]选择它们。

你也可以像这样定义大小的集团:bloc122=ast[...,0,:2,:2]bloc122.shape(3, 3, 4, 2, 2)):

In [8]: bloc122[[0,1,2],[0,1,0],[0,2,2]]=0

In [9]: a
Out[9]: 
array([[[ 0,  0,  2,  3],
        [ 0,  0,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17,  0,  0],
        [20, 21,  0,  0]],

       [[24, 25,  0,  0],
        [28, 29,  0,  0],
        [32, 33, 34, 35]]])

答案 1 :(得分:2)

第二维上的重叠块需要as_strided之类的内容。 B.M.as_strided的早期解决方案,但我发现很难理解。它看起来也很危险,因为它显示越界数据。我正在以较小的步骤接近这项任务。

沿着最后一个维度选择' cols' [0,1]和[2,3]相对容易。重塑使其变得更容易。

In [27]: A=np.arange(36).reshape(3,3,4)
In [28]: A1=A.reshape(3,3,2,2)
In [29]: A2=A1[[0,1,2],:,[0,1,1],:]

In [30]: A2
Out[30]: 
array([[[ 0,  1],
        [ 4,  5],
        [ 8,  9]],

       [[14, 15],
        [18, 19],
        [22, 23]],

       [[26, 27],
        [30, 31],
        [34, 35]]])

关注一个子阵列,我发现我可以看到它是2个重叠阵列:

In [59]: as_strided(A2[0],shape=(2,2,2),strides=(8,8,4))
Out[59]: 
array([[[0, 1],
        [4, 5]],

       [[4, 5],
        [8, 9]]])

np.lib.stride_tricks.as_strided是一个棘手的功能。我发现它主要用于移动窗口应用程序。

应用于整个阵列:

In [65]: A3=as_strided(A2,shape=(3,2,2,2),strides=(24,8,8,4))

In [66]: A3
Out[66]: 
array([[[[ 0,  1],
         [ 4,  5]],
         ...
        [[30, 31],
         [34, 35]]]])

目标:

In [71]: A3[[0,1,2],[0,1,0]]
Out[71]: 
array([[[ 0,  1],
        [ 4,  5]],

       [[18, 19],
        [22, 23]],

       [[26, 27],
        [30, 31]]])

这可以通过允许分配的方式放在一起(形状和步幅根据A1的值进行调整)

In [105]: A1 = A.reshape(3,3,2,2)
In [106]: A1s = as_strided(A1, shape=(3,2,2,2,2), strides=(48,16,16,8,4))

In [107]: A1s[[0,1,2],[0,1,0],:,[0,1,1],:]
Out[107]: 
array([[[ 0,  1],
        [ 4,  5]],

       [[18, 19],
        [22, 23]],

       [[26, 27],
        [30, 31]]])

作业测试:

In [108]: A1s[[0,1,2],[0,1,0],:,[0,1,1],:] = 99

In [109]: A
Out[109]: 
array([[[99, 99,  2,  3],
        [99, 99,  6,  7],
        [ 8,  9, 10, 11]],

       [[12, 13, 14, 15],
        [16, 17, 99, 99],
        [20, 21, 99, 99]],

       [[24, 25, 99, 99],
        [28, 29, 99, 99],
        [32, 33, 34, 35]]])

如果您不需要分配,则在没有striding的情况下更容易理解逻辑:

np.array([A2[0,:-1], A2[1,1:], A2[2,:-1]])

slices=(slice(-1),slice(1,None))    
np.array([A2[i,slices[j]] for i,j in zip([0,1,2],[0,1,0])])

===========================

早些时候磕磕绊绊地回答:

我认为可以使用高级索引来提取它。

Ind = np.ix_([0,0,1,1,2,2], [0,1,1,2,0,1], [0,1,2,3,2,3])
a[ind]

值可能需要调整,因为我无法在此进行测试。

我们的想法是枚举每个维度中需要哪些行,列,并使用ix_(我认为这是正确的函数)来添加newaxis,以便它们一起广播。< / p>

您对2d案例进行概括的想法是正确的。当需要np.array([0,1,2])时,以及当您需要使用ix_[:,None]等旋转它时,需要弄清楚。我最终测试了各种想法。

可能需要

  Ind = np.ix_([0,1,2], [0,1,1,2,0,1], [0,1,2,3,2,3])

不,这不是很正确。它会产生3x6x6;你想要3x2x2。

重塑为3x3x2x2可能会使最后一个维度的索引更容易。

另一个答案中提到的跨越技巧可能有助于将第二个重叠选择转换为类似的2x2块。但是我不得不玩这个。

我的成像是([1,2,3],[?,?,?],:,[?,?,?],:)

形式的索引元组

答案 2 :(得分:0)

您可以使用索引来分别获得预期的输出:

>>> a = np.array([[[ 0, 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]]])

>>> a[0,:2,:2]
array([[0, 1],
       [4, 5]])
>>> a[1,1:,2:]
array([[18, 19],
       [22, 23]])
>>> a[2,:2,2:]
array([[26, 27],
       [30, 31]])
>>> 

答案 3 :(得分:0)

当问这个问题时,我没想到它会使用hpaulj和BM提到的ix_as_strided等棘手的方法,这对我这样的初学者来说很难理解。

我想出了一个更容易理解但不那么有效的方式,灵感来自B. M。&#39>

a[[0,1,2],[0:2,1:3,0:2],[0:2,2:4,2:4]]

advanced indexing合并。

我们需要的是将a[[0,1,2],[0:2,1:3,0:2],[0:2,2:4,2:4]]翻译为a的高级索引。在我们的例子是:

a[[0 0 0 0 1 1 1 1 2 2 2 2],
  [0 0 1 1 1 1 2 2 0 0 1 1],
  [0 1 0 1 2 3 2 3 2 3 2 3]]

简单的翻译功能可以是这样的:

(代码中的笛卡尔函数来自pv。对this问题的回答)

# a[[0,1,2],[0:2,1:3,0:2],[0:2,2:4,2:4]]
def translate_idx(idx1=[0, 1, 2], idx2=[(0,2),(1,3),(0,2)], idx3=[(0,2),(2,4),(2,4)]):
    # first we need to get the combinations for correponding intervals
    # e.g for (1,3) and (2,4) we get [[1, 2], [1, 3], [2, 2], [2, 3]]
    idx23 = []
    for i in range(len(idx2)):
        # the function cartesian here just generates all conbinations from some arrays
        idx23.append(cartesian((np.arange(*idx2[i]), np.arange(*idx3[i]))))
    idx23 = np.concatenate(idx23).T
    # now we get index for 2nd and 3rd axis
    # [[0 0 1 1 1 1 2 2 0 0 1 1]
    #  [0 1 0 1 2 3 2 3 2 3 2 3]]
    # we can repeat 4 times for idx1 and append idx23
    step = (idx2[0][1] - idx2[0][0]) * (idx3[0][1] - idx3[0][0])
    idx123 = np.append(np.array(idx1).repeat(step).reshape((1, -1)), idx23, axis=0)
    return idx123

然后我可以使用

idx = translate_idx()
a[idx[0], idx[1], idx[2]]

得到我想要的东西。

虽然这种方法效率不高,但我认为它揭示了这类问题与高级索引之间的一些关联。在实践中as_strided绝对是更好的选择。

谢谢所有人的详细答案:)