在 NumPy 中将 4D 数组重塑为 2D 数组背后的直觉和想法

Posted

技术标签:

【中文标题】在 NumPy 中将 4D 数组重塑为 2D 数组背后的直觉和想法【英文标题】:Intuition and idea behind reshaping 4D array to 2D array in NumPy 【发布时间】: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 的一些解释将非常有帮助。谢谢!

【问题讨论】:

【参考方案1】:

ndnd 转换的总体思路

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

置换轴(如果需要的置换顺序是滚动的,则使用numpy.transposenumpy.moveaxisnumpy.rollaxis,如果只需要交换两个轴,则使用numpy.swapaxes)和

重塑。

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

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

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

    [ 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 在任何地方都完成了这项工作。它们在下面列出:

Python Reshape 3d array into 2d reshape an array using python/numpy Merging non-overlapping array blocks Conversion from a Numpy 3D array to a 2D array how to reshape an N length vector to a 3x(N/3) matrix in numpy using reshape Construct image from 4D list Reshaping/Combining several sub-matrices to one matrix in multi-dimensional space Interlace various small 2D matrices into a bigger one how to retrieve every section by 3X3? Reshaping 3D Numpy Array to a 2D array Iterate in submatrices through a bigger matrix Reorganizing a 2D numpy array into 3D Numpy change shape from (3, 512, 660, 4) to (3,2048,660,1) Numpy: rotate sub matrix m of M Split a 3D numpy array into 3D blocks Converting 3D matrix to cascaded 2D Matrices Rearranging numpy array Numpy: Reshape array along a specified axis How to construct 2d array from 2d arrays How to form a matrix from submatrices? Python: Reshape 3D image series to pixel series

【讨论】:

【参考方案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 不足以涵盖我的情况,我必须做一些更复杂的事情。

【讨论】:

以上是关于在 NumPy 中将 4D 数组重塑为 2D 数组背后的直觉和想法的主要内容,如果未能解决你的问题,请参考以下文章

Numpy 用 1 列将 1d 重塑为 2d 数组

将 2D numpy 数组重塑为 3 个具有 x,y 索引的 1D 数组

Python将3d数组重塑为2d

在 python 中将 4D 数组与 2D 数组相乘和求和的最快方法?

如何在 NumPy 中将 HDF5 2D 数组转换为 1D?

如何将 numpy 数组重塑为 3 维以输入到卷积层?