【问题标题】:Intuition and idea behind reshaping 4D array to 2D array in NumPy在 NumPy 中将 4D 数组重塑为 2D 数组背后的直觉和想法
【发布时间】:2018-06-07 05:12:02
【问题描述】:

在实现Kronecker-product 时出于教学原因(没有使用显而易见且现成的np.kron()),我获得了一个 4 维数组作为中间结果,我必须将其重塑为得到最终结果。

但是,我仍然无法专注于重塑这些高维数组。我有这个4D 数组:

array([[[[ 0,  0],
         [ 0,  0]],

        [[ 5, 10],
         [15, 20]]],


       [[[ 6, 12],
         [18, 24]],

        [[ 7, 14],
         [21, 28]]]])

这是(2, 2, 2, 2) 的形状,我想将其重塑为(4,4)。有人可能会认为这很明显与

np.reshape(my4darr, (4,4))

但是,上面的重塑并没有给我预期的结果,即:

array([[ 0,  5,  0, 10],
       [ 6,  7, 12, 14],
       [ 0, 15,  0, 20],
       [18, 21, 24, 28]])

如您所见,预期结果中的所有元素都存在于4D 数组中。我只是无法根据需要正确地进行 reshape。除了答案之外,有关如何为此类高维数组执行reshape 的一些解释将非常有帮助。谢谢!

【问题讨论】:

    标签: python arrays numpy multidimensional-array reshape


    【解决方案1】:

    ndnd 转换的总体思路

    这种ndnd 转换的想法只使用了两件事-

    置换轴: 获得顺序,使得展平版本对应于展平版本的输出。所以,如果你最终以某种方式使用了它两次,请再看一遍,因为你不应该这样做。

    重塑:分割轴或将最终输出带到所需的形状。分割轴主要在开始时需要,当输入是低亮度并且我们需要分割成块时。同样,您不应该超过两次。

    因此,通常我们将分为三个步骤:

        [ Reshape ]      --->  [ Permute axes ]   --->  [ Reshape ]
    
     Create more axes             Bring axes             Merge axes
                              into correct order
    

    回溯法

    考虑到输入和输出,最安全的解决方法是通过所谓的回溯方法,即拆分输入的轴(从较小的nd 到较大的nd)或分割输出的轴(从更大的nd 到更小的nd)。拆分的想法是使较小的nd 的暗淡数量与较大的nd 相同。然后,研究输出的步幅并将其与输入进行匹配以获得所需的置换顺序。最后,如果最后一个是较小的nd 一个,最后可能需要重新整形(默认方式或 C 顺序)来合并轴。

    如果输入和输出的维度数相同,那么我们需要将两者拆分并分成块并研究它们的步幅。在这种情况下,我们应该有块大小的额外输入参数,但这可能是题外话。

    示例

    让我们用这个具体案例来演示如何应用这些策略。在这里,输入是4D,而输出是2D。所以,很可能,我们不需要 reshape 来拆分。所以,我们需要从排列轴开始。由于最终输出不是4D,而是2D,所以我们需要在最后进行reshape。

    现在,这里的输入是:

    In [270]: a
    Out[270]: 
    array([[[[ 0,  0],
             [ 0,  0]],
    
            [[ 5, 10],
             [15, 20]]],
    
    
           [[[ 6, 12],
             [18, 24]],
    
            [[ 7, 14],
             [21, 28]]]])
    

    预期的输出是:

    In [271]: out
        Out[271]: 
        array([[ 0,  5,  0, 10],
               [ 6,  7, 12, 14],
               [ 0, 15,  0, 20],
               [18, 21, 24, 28]])
    

    此外,这是一个较大的 nd 到较小的 nd 转换,因此回溯方法将涉及拆分输出并研究其 strides 并匹配输入中的相应值:

                        axis = 3
                       ---      -->          
                                            
                        axis = 1                    
                       ------>           
    axis=2|  axis=0|   [ 0,  5,  0, 10],        
    
                   |   [ 6,  7, 12, 14],
                   v  
          |            [ 0, 15,  0, 20],
          v
                       [18, 21, 24, 28]])
    

    因此,所需的置换顺序是(2,0,3,1)

    In [275]: a.transpose((2, 0, 3, 1))
    Out[275]: 
    array([[[[ 0,  5],
             [ 0, 10]],
    
            [[ 6,  7],
             [12, 14]]],
    
    
           [[[ 0, 15],
             [ 0, 20]],
    
            [[18, 21],
             [24, 28]]]])
    

    然后,简单地重塑为预期的形状:

    In [276]: a.transpose((2, 0, 3, 1)).reshape(4,4)
    Out[276]: 
    array([[ 0,  5,  0, 10],
           [ 6,  7, 12, 14],
           [ 0, 15,  0, 20],
           [18, 21, 24, 28]])
    

    更多示例

    我挖掘了我的历史,发现很少有基于ndnd 转换的Q&As。这些可以作为其他示例案例,尽管解释较少(大部分)。如前所述,最多两个reshapes 和最多一个swapaxes/transpose 在任何地方都完成了这项工作。它们在下面列出:

    【讨论】:

      【解决方案2】:

      您似乎在寻找transpose,然后是reshape

      x.transpose((2, 0, 3, 1)).reshape(np.prod(x.shape[:2]), -1)
      
      array([[ 0,  5,  0, 10],
             [ 6,  7, 12, 14],
             [ 0, 15,  0, 20],
             [18, 21, 24, 28]])
      

      为了帮助您了解为什么需要转置,让我们分析一下形状不正确的输出(通过单个 reshape 调用获得)以了解它为什么不正确。

      这个结果的一个简单的 2D 重塑版本(没有任何转置)看起来像这样 -

      x.reshape(4, 4)
      
      array([[ 0,  0,  0,  0],
             [ 5, 10, 15, 20],
             [ 6, 12, 18, 24],
             [ 7, 14, 21, 28]])
      

      现在根据您的预期输出考虑这个输出 -

      array([[ 0,  5,  0, 10],
             [ 6,  7, 12, 14],
             [ 0, 15,  0, 20],
             [18, 21, 24, 28]])
      

      您会注意到,您的实际结果是通过对形状不正确的输出进行 Z 状遍历获得的 -

      start
          | /|     /| /|
          |/ |    / |/ |
            /    /    / 
           /    /    /
          | /| /    | /|
          |/ |/     |/ |
                       end
      

      这意味着您必须以不同的步幅在数组上移动才能获得 实际 结果。总之,简单的重塑是不够的。您必须转置原始数组,以使这些类似 Z 的元素彼此连续,以便随后的 reshape 调用为您提供所需的输出。

      要了解如何正确转置,您应该沿着输入跟踪元素并找出需要跳转到输出中的每个轴的轴。换位随之而来。 Divakar's answer 很好地解释了这一点。

      【讨论】:

      • ... 或者只是在转置过程中翻转它>
      • @juanpa.arrivillaga 你为什么要删除?看起来不错。
      • 因为只使用.transpose(2, 0, 3, 1) 而不是.transpose(0,2,1,3)然后 重新整形,以fortran 顺序获取数组...
      • @cᴏʟᴅsᴘᴇᴇᴅ 需要使用您的解决方案来解释一般情况。希望没关系。
      • @kmario23 没问题。我的解释与 Divakar 的角度不同,因为我想纠正您的误解,即简单的重塑就足够了。为此,我开始分析错误重塑的输出而不是原始输入。我对接受没有任何抱怨,他的回答是黄金标准。
      【解决方案3】:

      The Divarkar's answer is great,尽管有时我更容易检查transposereshape 涵盖的所有可能情况。

      比如下面的代码

      n, m = 4, 2
      arr = np.arange(n*n*m*m).reshape(n,n,m,m)
      for permut in itertools.permutations(range(4)):
          arr2 = (arr.transpose(permut)).reshape(n*m, n*m)
          print(permut, arr2[0])
      

      为我提供了使用 transpose + reshape 从 4 维数组中可以获得的所有信息。因为,我知道输出应该是什么样子,所以我将选择显示正确答案的排列。如果我没有得到我想要的,那么transpose + reshape 不足以涵盖我的情况,我必须做一些更复杂的事情。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2018-03-17
        • 1970-01-01
        • 2018-05-12
        • 2016-05-22
        • 2021-08-06
        • 2017-09-18
        • 1970-01-01
        相关资源
        最近更新 更多