【问题标题】:Manipulating numpy arrays (concatenating inner sub-arrays)操作 numpy 数组(连接内部子数组)
【发布时间】:2021-10-31 02:04:35
【问题描述】:

我有一个操作 numpy 数组的问题。比如说,给定一个np.array([[[1,2],[3,4]], [[5,6],[7,8]]]) 形式的 3-d 数组,它是一个 (2,2,2) 数组。我想将它操纵成一个(2,4) 数组,这样a = np.array([[1,2,5,6],[3,4,7,8]])。我想知道有没有 numpy 的内置方法,特别是处理这样的问题并且可以很容易地概括。

已编辑: 谢谢大家的回答。他们都摇滚!我想我应该澄清我在原始帖子中“容易概括”的意思。假设给定一个(6,3,2,3) 数组(这是我面临的实际挑战)

a = array([[[[ 10,  20,  30],
         [ 40,  40,  20]],

        [[ 22,  44,  66],
         [ 88,  88,  44]],

        [[ 33,  66,  99],
         [132, 132,  66]]],


       [[[ 22,  44,  66],
         [ 88,  88,  44]],

        [[ 54, 108, 162],
         [216, 216, 108]],

        [[ 23,  46,  69],
         [ 92,  92,  46]]],


       [[[ 14,  28,  42],
         [ 56,  56,  28]],

        [[ 25,  50,  75],
         [100, 100,  50]],

        [[ 33,  66,  99],
         [132, 132,  66]]],


       [[[ 20,  40,  60],
         [ 80,  80,  40]],

        [[ 44,  88, 132],
         [176, 176,  88]],

        [[ 66, 132, 198],
         [264, 264, 132]]],


       [[[ 44,  88, 132],
         [176, 176,  88]],

        [[108, 216, 324],
         [432, 432, 216]],

        [[ 46,  92, 138],
         [184, 184,  92]]],


       [[[ 28,  56,  84],
         [112, 112,  56]],

        [[ 50, 100, 150],
         [200, 200, 100]],

        [[ 66, 132, 198],
         [264, 264, 132]]]])

我想将它按摩成一个 (3,3,2,2,3) 数组,这样 fora[0,:,:,:,:]

a[0,0,0,:,:] = np.array([[10,20,30],[40,40,20]]);
a[0,1,0,:,:] = np.array([[22,44,66],[88,88,44]]);
a[0,2,0,:,:] = np.array([[33,66,99],[132,132,66]]);
a[0,0,1,:,:] = np.array([[20,40,60],[80,80,40]]);
a[0,1,1,:,:] = np.array([[44,88,132],[176,176,88]]);
a[0,2,1,:,:] = np.array([[66,132,198],[264,264,132]]).

简而言之,最后 3 个最大的块应该与前 3 个最大的块“合并”以形成 3 个(3,2) 块。其余 2 个块,即 (a[1,:,:,:,:], a[2,:,:,:,:]) 遵循相同的模式。

【问题讨论】:

  • numpy.reshape
  • 小心我问的问题,reshape 并不能解决我的问题。 reshape 只会给我np.array([[1,2,3,4],[5,6,7,8]])
  • 我不明白np.reshape(a, (2,4)) 没有产生您在问题中显示的预期输出。
  • 哦,没关系,你在搞乱其中的值的顺序。好吧,那很微妙。也许您需要先对数组的某些部分进行一些转置?但是你的换位肯定不是标准的,所以我不知道如何完成。
  • 我尝试了很多技巧,这个问题比想象的要难。否则,我不会费心发布这个问题。

标签: python arrays numpy


【解决方案1】:

我认为在这种情况下(第一个例子),简单地说:

>>> a.swapaxes(0, 1).reshape(2, -1)
array([[1, 2, 5, 6],
       [3, 4, 7, 8]])

一般来说,我发现@Divakar mini tutorial 是此类操作的权威来源。

编辑问题更新后(包含一个更大数组的示例),我为这类问题编写了一个小型求解器(实际上相当快)。

以下任何一项都会产生相同的结果,符合约束:

np.moveaxis(a.reshape(2, 3, 3, 2, 3), 0, 2)
np.rollaxis(a.reshape(2, 3, 3, 2, 3), 0, 3)
a.reshape(2, 3, 3, 2, 3).transpose(1, 2, 0, 3, 4)
a.reshape(2, 9, 6).swapaxes(0, 1).reshape(3, 3, 2, 2, 3)
np.rollaxis(a.reshape(2, 9, 6), 1).reshape(3, 3, 2, 2, 3)
a.reshape(2, 9, 2, 3).swapaxes(0, 1).reshape(3, 3, 2, 2, 3)
a.reshape(2, 9, 3, 2).swapaxes(0, 1).reshape(3, 3, 2, 2, 3)
a.reshape(2, 9, 6).transpose(1, 0, 2).reshape(3, 3, 2, 2, 3)
# ...

当然,您可以决定将.reshape() 中的任何单个值更改为-1,以“使其更通用”或更直观。例如:

np.rollaxis(a.reshape(2, 3, 3, 2, -1), 0, 3)

【讨论】:

    【解决方案2】:

    从您的新更新中,您可以使用 np.lib.stride_tricks.as_strided 执行以下操作:

    >>> np.lib.stride_tricks.as_strided(a, shape=(3,3,2,2,3), strides=(72,24,216,12,4))
    array([[[[[ 10,  20,  30],
              [ 40,  40,  20]],
    
             [[ 20,  40,  60],
              [ 80,  80,  40]]],
    
    
            [[[ 22,  44,  66],
              [ 88,  88,  44]],
    
             [[ 44,  88, 132],
              [176, 176,  88]]],
    
    
            [[[ 33,  66,  99],
              [132, 132,  66]],
    
             [[ 66, 132, 198],
              [264, 264, 132]]]],
    
    
    
           [[[[ 22,  44,  66],
              [ 88,  88,  44]],
    
             [[ 44,  88, 132],
              [176, 176,  88]]],
    
    
            [[[ 54, 108, 162],
              [216, 216, 108]],
    
             [[108, 216, 324],
              [432, 432, 216]]],
    
    
            [[[ 23,  46,  69],
              [ 92,  92,  46]],
    
             [[ 46,  92, 138],
              [184, 184,  92]]]],
    
    
    
           [[[[ 14,  28,  42],
              [ 56,  56,  28]],
    
             [[ 28,  56,  84],
              [112, 112,  56]]],
    
    
            [[[ 25,  50,  75],
              [100, 100,  50]],
    
             [[ 50, 100, 150],
              [200, 200, 100]]],
    
    
            [[[ 33,  66,  99],
              [132, 132,  66]],
    
             [[ 66, 132, 198],
              [264, 264, 132]]]]])
    

    解释:

    再举一个例子:一个小数组q和更改q后我们想要的输出:

    >>> q = np.arange(12).reshape(4,3,-1)
    >>> q
    array([[[ 0],
            [ 1],
            [ 2]],
    
           [[ 3],
            [ 4],
            [ 5]],
    
           [[ 6],
            [ 7],
            [ 8]],
    
           [[ 9],
            [10],
            [11]]])
    # desired output:
    # shape = (2, 3, 2)
    array([[[ 0,  6],
            [ 1,  7],
            [ 2,  8]],
    
           [[ 3,  9],
            [ 4, 10],
            [ 5, 11]]])
    

    这里我们使用numpy strides 来实现这一点。让我们检查q 的进步:

    >>> q.strides
    (12, 4, 4)
    

    在我们的输出中,除了第三步之外,所有步幅都应该保持不变,因为在第三维中,我们需要与 q 的下半部分的值进行堆叠,即:6 放在 @987654331 旁边@、7 旁边是 1 等等...

    那么,从 06 有多“远”?或者换句话说,从q[0,0,0]q[2,0,0]有多远?

    # obviously, distance = [2,0,0] - [0,0,0] = [2,0,0]
    bytedistance = np.sum(np.array([2,0,0])*q.strides)
    # 2*12 + 0*4 + 0*4 = 24 bytes
    

    好的,然后new_strides = (12, 4, 24),因此我们得到了:

    >>> np.lib.stride_tricks.as_strided(q, shape=(2,3,2), strides=new_strides)
    array([[[ 0,  6],
            [ 1,  7],
            [ 2,  8]],
    
           [[ 3,  9],
            [ 4, 10],
            [ 5, 11]]])
    

    回到你的问题:

    a.strides = (72,24,12,4)
    new_strides = (72,24,216,12,4)     # why is 216 here ? it's a homework :)
    new_a = np.lib.stride_tricks.as_strided(a, shape=(3,3,2,2,3), strides=new_strides)
    

    【讨论】:

    • 你的答案很震撼。在我的情况下,我会接受它作为最普遍的解决方案。
    • @CoolGas 我建议您通读上面链接中的文档:as_strided 如果使用不当会被认为是危险的。您需要仔细计算步幅,否则您的程序可能会崩溃。
    • 是的,完全同意。 numpy 页面确实强调了这种方法只能在极端情况下使用。感谢您指出这一点。
    【解决方案3】:

    首先交换轴,使用np.swapaxes,然后再整形得到输出。

    import numpy as np
    a = np.array([[[1,2],[3,4]], [[5,6],[7,8]]])
    a = np.swapaxes(a, 0, 1)
    a = np.reshape(a, (2, 4))
    print(a)
    

    输出

    [[1 2 5 6]
     [3 4 7 8]]
    

    您还可以使用 np.transpose(如 np.transpose(a, (1, 0, 2)))将轴从 (0, 1, 2) 交换到 (1, 0, 2),正如 MadPhysicist 所指出的那样。

    【讨论】:

    • 你也可以使用.transpose(1, 0, 2)
    • @MadPhysicist 我已经用建议更新了答案。
    【解决方案4】:

    您的主题行回答了您的问题:

    In [813]: a
    Out[813]: 
    array([[[1, 2],
            [3, 4]],
    
           [[5, 6],
            [7, 8]]])
    In [818]: np.concatenate(a, axis=1)    # aka np.hstack
    Out[818]: 
    array([[1, 2, 5, 6],
           [3, 4, 7, 8]])
    

    这会将数组视为 2 (2,2) 个子数组。

    另一个连接选项:

    In [819]: np.concatenate(a, axis=0)
    Out[819]: 
    array([[1, 2],
           [3, 4],
           [5, 6],
           [7, 8]])
    

    我认为转置后跟 reshape 更好,更容易泛化。但它需要了解数组的存储方式、维度的含义和转置方式。

    普通reshape 不起作用的原因是您想要重新排列数组的元素。

    如文档所述,reshape 有效地分解数组,然后应用新形状:

    In [823]: a.ravel()
    Out[823]: array([1, 2, 3, 4, 5, 6, 7, 8])
    

    但是你的新数组有不同的顺序:

    In [824]: np.concatenate(a, axis=1).ravel()
    Out[824]: array([1, 2, 5, 6, 3, 4, 7, 8])
    

    【讨论】:

      【解决方案5】:

      你可以使用 hstack 和 vstack。

          a= np.array([[[1,2],[3,4]], [[5,6],[7,8]]])
          s0, s1 = range(a.shape[0])
          
          x= np.vstack(np.hstack((a[s0], a[s1])))
          print(x)
      

      输出

      [[1 2 5 6]
       [3 4 7 8]]
      

      【讨论】:

      • np.hstack(a) 就是您所需要的。它将数组视为 2 (2,2) 个数组的列表。
      【解决方案6】:

      numpy.reshape 结合zip 可以做你想做的事,但有点不靠谱:

      >>> a = np.array([[[1,2],[3,4]], [[5,6],[7,8]]])
      >>> b = np.array(list(zip(a[0], a[1])))
      >>> np.reshape(b, (2,4))
      array([[1, 2, 5, 6],
             [3, 4, 7, 8]])
      

      挑战在于您要转换 a 的第一维和第二维,这不是 np.transpose 所做的。但是,zip 可以有效地做到这一点。

      【讨论】:

        猜你喜欢
        • 2019-07-31
        • 1970-01-01
        • 2016-06-06
        • 1970-01-01
        • 2018-11-30
        • 1970-01-01
        • 2013-08-22
        • 2018-12-26
        相关资源
        最近更新 更多