用NumPy将4D数组重新修改为2D数组背后的直觉和想法

时间:2022-04-01 01:45:38

While implementing a Kronecker-product for pedagogical reasons (without using the obvious and readily available np.kron()), I obtained a 4 dimensional array as an intermediate result, which I've to reshape to get the final result.

在出于教学原因实现Kronecker-product时(不使用明显且现成的np.kron())),我获得了一个4维数组作为中间结果,我必须对其进行重新组合才能得到最终结果。

But, I still can't wrap my head around reshaping these high dimensional arrays. I have this 4D array:

但是,我仍然不能把我的头脑集中在重塑这些高维数组上。我有这个4D数组

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

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


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

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

This is of shape (2, 2, 2, 2) and I'd like to reshape it to (4,4). One might think that this is obvious to do with

这是形状为(2,2,2,2,2,2)我想把它重塑为(4,4)有人可能会认为这显然与此有关

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

But, the above reshape does not give me the expected result which is:

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

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

As you can see, all the elements in the expected result are present in the 4D array. I just can't get the hang of doing the reshape correctly as needed. In addition to the answer, some explanation of how to do the reshape for such high dimensional arrays would be really helpful. Thanks!

如您所见,预期结果中的所有元素都显示在4D数组中。我只是不知道如何在需要的时候正确地进行重塑。除了答案之外,对如何为这样的高维数组进行重新设计进行一些解释将会非常有帮助。谢谢!

2 个解决方案

#1


20  

General idea for nd to nd transformation

The idea with such nd to nd transformation is using just two things - Permute axes (with numpy.transpose or numpy.rollaxis if the needed permute order is a rolled one or numpy.swapaxes if just two axes need to be swapped) and Reshape.

这种nd到nd转换的想法是使用两个东西——置换轴(使用numpy)。置或numpy。如果需要的排列顺序是滚动的或者是numpy,那么可以使用rollaxis。如果只需要交换两个轴,则交换swapaxes并重新组合。

Permute axes : To get the order such that the flattened version corresponds to the flattened version of output. So, if you somehow end up using it twice, look again because you shouldn't.

排列坐标轴:为了获得使变平版本对应于输出的变平版本的顺序。所以,如果你最终用了两次,再看一次,因为你不应该这样做。

Reshape : To split the axes or bring the final output to the desired shape. Splitting axes is needed mostly at the start, when the input is of lower-dim and we are needed to split into blocks. Again, you shouldn't need this more than twice.

重形:将轴分开或将最终输出带至所需的形状。在开始时需要分割轴,当输入是低暗的,我们需要分割成块。同样,你不应该需要超过两次。

Hence, generally we would have three steps :

因此,我们通常有三个步骤:

    [ Reshape ]      --->  [ Permute axes ]   --->  [ Reshape ]

 Create more axes             Bring axes             Merge axes
                          into correct order

Back-tracking method

回溯法

The safest way to solve, given the input and output is through, what one could call as the back-tracking method, i.e. split the axes of the input (when going from smaller nd to bigger nd) or split the axes of the output (when going from bigger nd to smaller nd). The idea with the splitting is to bring the number of dims of the smaller nd one same as the bigger nd one. Then, study the strides of the output and match it up against the input to get the required permute order. Finally, a reshape (default way or C order) might be needed at the end, if the final one is a smaller nd one, to merge axes.

考虑到输入和输出,最安全的解决方法是通过所谓的反向跟踪方法,即分割输入轴(从较小的nd到较大的nd)或分割输出轴(从较大的nd到较小的nd)。分裂的想法是把较小的和较大的一样的dims的数目。然后,研究输出的步长,并将其与输入匹配,得到所需的排列顺序。最后,如果最后一个是较小的nd,那么在最后可能需要一个整形(默认方式或C阶)来合并坐标轴。

If both input and output are of same number of dims, then we would need to split both and break into blocks and study their strides against each other. In such cases, we should have the additional input parameter of block sizes, but that's probably off-topic.

如果输入和输出都是相同数量的dims,那么我们就需要将两者分开,然后分解成块,然后研究它们之间的距离。在这种情况下,我们应该有块大小的额外输入参数,但这可能是离题的。

Example

Let's use this specific case to demonstrate how to apply those strategies. In here, the input is 4D, while output is 2D. So, most probably, we won't need reshape to split. So, we need to start with permuting axes. Since, the final output is not 4D, but a 2D one, we would need a reshape at the end.

让我们用这个具体的例子来演示如何应用这些策略。在这里输入是4D,而输出是2D。因此,最可能的情况是,我们不需要重新设计来进行拆分。我们需要从排列坐标轴开始。由于最终输出不是4D,而是2D,所以我们需要在最后进行重新设计。

Now, the input here is :

这里的输入是:

In [270]: a
Out[270]: 
array([[[[ 0,  0],
         [ 0,  0]],

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


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

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

The expected output is :

预期产出为:

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

Also, this is a bigger nd to smaller nd transformation, so the back-tracking method would involve, splitting the output and studying its strides and matching up against the corresponding values in input :

同时,这是一个更大的nd到更小的nd转换,因此回溯方法将涉及到,分割输出,研究其步幅,并与输入的对应值匹配:

                    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]])

Hence, the permuted order needed is (2,0,3,1) :

因此,所需的排列顺序为(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]]]])

Then, simply reshape to the expected shape :

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

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]])

More examples

更多的例子

I dug up my history and found few Q&As based on nd to nd transformations. These could serve as other example cases, albeit with lesser explanation (mostly). As mentioned earlier, at most two reshapes and at most one swapaxes/transpose did the job everywhere. They are listed below :

我挖掘了我的历史,发现很少有Q&As基于nd和nd的转换。这些可以作为其他的例子,尽管有较少的解释(主要是)。正如前面所提到的,在大多数情况下,两个重构和最多一个swapax /转置都可以完成所有的工作。以下列出:

#2


9  

It seems like you're looking for a transpose followed by a 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]])

To help you understand why a transposition is needed, let's analyse your incorrectly shaped output (obtained by a single reshape call) to understand why it is incorrect.

为了帮助您理解为什么需要换位,让我们分析您不正确的形状输出(通过一次重新配置调用获得),以理解它为什么不正确。

A simple 2D reshaped version of this result (without any transposition) looks like this -

这个结果的一个简单的2D重构版本(没有任何换位)看起来像这样

x.reshape(4, 4)

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

Now consider this output with respect to your expected output -

现在考虑这个输出与您期望的输出-。

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

You'll notice that your actual result is obtained by a Z-like traversal of your incorrectly shaped output -

您将注意到,您的实际结果是通过类似z的遍历错误形状的输出-获得的

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

This implies that you must move over the array in varying strides to get your actual result. In conclusion, a simple reshape is not enough. You must transpose the original array, in such a manner that these Z-like elements are made to be contiguous to each other, such that a subsequent reshape call gives you the output you need.

这意味着您必须在不同的步骤中移动数组以获得实际的结果。总之,简单的改造是不够的。您必须对原始数组进行转置,以使这些类z的元素彼此相邻,以便后续的重新配置调用能够提供您所需的输出。

To understand how to transpose correctly, you should trace the elements along the input and figure out what axes you need to jump to get to each one in the output. The transposition follows accordingly. Divakar's answer does a sterling job of explaining this.

要理解如何正确地进行转置,您应该沿着输入跟踪元素,并找出需要跳转到输出中的每个坐标轴。接下来的换位。迪瓦卡的回答很好地解释了这一点。

#1


20  

General idea for nd to nd transformation

The idea with such nd to nd transformation is using just two things - Permute axes (with numpy.transpose or numpy.rollaxis if the needed permute order is a rolled one or numpy.swapaxes if just two axes need to be swapped) and Reshape.

这种nd到nd转换的想法是使用两个东西——置换轴(使用numpy)。置或numpy。如果需要的排列顺序是滚动的或者是numpy,那么可以使用rollaxis。如果只需要交换两个轴,则交换swapaxes并重新组合。

Permute axes : To get the order such that the flattened version corresponds to the flattened version of output. So, if you somehow end up using it twice, look again because you shouldn't.

排列坐标轴:为了获得使变平版本对应于输出的变平版本的顺序。所以,如果你最终用了两次,再看一次,因为你不应该这样做。

Reshape : To split the axes or bring the final output to the desired shape. Splitting axes is needed mostly at the start, when the input is of lower-dim and we are needed to split into blocks. Again, you shouldn't need this more than twice.

重形:将轴分开或将最终输出带至所需的形状。在开始时需要分割轴,当输入是低暗的,我们需要分割成块。同样,你不应该需要超过两次。

Hence, generally we would have three steps :

因此,我们通常有三个步骤:

    [ Reshape ]      --->  [ Permute axes ]   --->  [ Reshape ]

 Create more axes             Bring axes             Merge axes
                          into correct order

Back-tracking method

回溯法

The safest way to solve, given the input and output is through, what one could call as the back-tracking method, i.e. split the axes of the input (when going from smaller nd to bigger nd) or split the axes of the output (when going from bigger nd to smaller nd). The idea with the splitting is to bring the number of dims of the smaller nd one same as the bigger nd one. Then, study the strides of the output and match it up against the input to get the required permute order. Finally, a reshape (default way or C order) might be needed at the end, if the final one is a smaller nd one, to merge axes.

考虑到输入和输出,最安全的解决方法是通过所谓的反向跟踪方法,即分割输入轴(从较小的nd到较大的nd)或分割输出轴(从较大的nd到较小的nd)。分裂的想法是把较小的和较大的一样的dims的数目。然后,研究输出的步长,并将其与输入匹配,得到所需的排列顺序。最后,如果最后一个是较小的nd,那么在最后可能需要一个整形(默认方式或C阶)来合并坐标轴。

If both input and output are of same number of dims, then we would need to split both and break into blocks and study their strides against each other. In such cases, we should have the additional input parameter of block sizes, but that's probably off-topic.

如果输入和输出都是相同数量的dims,那么我们就需要将两者分开,然后分解成块,然后研究它们之间的距离。在这种情况下,我们应该有块大小的额外输入参数,但这可能是离题的。

Example

Let's use this specific case to demonstrate how to apply those strategies. In here, the input is 4D, while output is 2D. So, most probably, we won't need reshape to split. So, we need to start with permuting axes. Since, the final output is not 4D, but a 2D one, we would need a reshape at the end.

让我们用这个具体的例子来演示如何应用这些策略。在这里输入是4D,而输出是2D。因此,最可能的情况是,我们不需要重新设计来进行拆分。我们需要从排列坐标轴开始。由于最终输出不是4D,而是2D,所以我们需要在最后进行重新设计。

Now, the input here is :

这里的输入是:

In [270]: a
Out[270]: 
array([[[[ 0,  0],
         [ 0,  0]],

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


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

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

The expected output is :

预期产出为:

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

Also, this is a bigger nd to smaller nd transformation, so the back-tracking method would involve, splitting the output and studying its strides and matching up against the corresponding values in input :

同时,这是一个更大的nd到更小的nd转换,因此回溯方法将涉及到,分割输出,研究其步幅,并与输入的对应值匹配:

                    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]])

Hence, the permuted order needed is (2,0,3,1) :

因此,所需的排列顺序为(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]]]])

Then, simply reshape to the expected shape :

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

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]])

More examples

更多的例子

I dug up my history and found few Q&As based on nd to nd transformations. These could serve as other example cases, albeit with lesser explanation (mostly). As mentioned earlier, at most two reshapes and at most one swapaxes/transpose did the job everywhere. They are listed below :

我挖掘了我的历史,发现很少有Q&As基于nd和nd的转换。这些可以作为其他的例子,尽管有较少的解释(主要是)。正如前面所提到的,在大多数情况下,两个重构和最多一个swapax /转置都可以完成所有的工作。以下列出:

#2


9  

It seems like you're looking for a transpose followed by a 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]])

To help you understand why a transposition is needed, let's analyse your incorrectly shaped output (obtained by a single reshape call) to understand why it is incorrect.

为了帮助您理解为什么需要换位,让我们分析您不正确的形状输出(通过一次重新配置调用获得),以理解它为什么不正确。

A simple 2D reshaped version of this result (without any transposition) looks like this -

这个结果的一个简单的2D重构版本(没有任何换位)看起来像这样

x.reshape(4, 4)

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

Now consider this output with respect to your expected output -

现在考虑这个输出与您期望的输出-。

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

You'll notice that your actual result is obtained by a Z-like traversal of your incorrectly shaped output -

您将注意到,您的实际结果是通过类似z的遍历错误形状的输出-获得的

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

This implies that you must move over the array in varying strides to get your actual result. In conclusion, a simple reshape is not enough. You must transpose the original array, in such a manner that these Z-like elements are made to be contiguous to each other, such that a subsequent reshape call gives you the output you need.

这意味着您必须在不同的步骤中移动数组以获得实际的结果。总之,简单的改造是不够的。您必须对原始数组进行转置,以使这些类z的元素彼此相邻,以便后续的重新配置调用能够提供您所需的输出。

To understand how to transpose correctly, you should trace the elements along the input and figure out what axes you need to jump to get to each one in the output. The transposition follows accordingly. Divakar's answer does a sterling job of explaining this.

要理解如何正确地进行转置,您应该沿着输入跟踪元素,并找出需要跳转到输出中的每个坐标轴。接下来的换位。迪瓦卡的回答很好地解释了这一点。