在 NumPy 中将 4 维数组重塑为 2 维数组背后的直觉和想法
Intuition and idea behind reshaping 4D array to 2D array in NumPy
在出于教学原因实施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
的一些解释将非常有帮助。谢谢!
您似乎在寻找 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]])
为了帮助您理解为什么需要换位,让我们分析您的错误形状输出(通过单个 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 的元素彼此连续,以便后续的重塑调用为您提供所需的输出。
要了解如何正确转置,您应该跟踪输入中的元素并找出需要跳转哪些轴才能到达输出中的每个元素。相应地进行换位。 对此做了出色的解释。
nd
到 nd
转换的总体思路
这种 nd
到 nd
转换的想法只使用了两件事 -
置换轴(如果只需要交换两个轴,则使用 numpy.transpose
or numpy.moveaxis
or numpy.rollaxis
if the needed permute order is a rolled one or numpy.swapaxes
)和
整形。
Permute axes : 获取顺序,使得扁平化版本对应于扁平化版本的输出。所以,如果你不知何故最终使用它两次,再看一遍,因为你不应该。
Reshape : 拆分轴或将最终输出调整为所需的形状。拆分轴主要是在开始时需要的,当输入是 lower-dim 并且我们需要拆分成块时。同样,你不应该需要两次以上。
因此,一般我们会分为三个步骤:
[ Reshape ] ---> [ Permute axes ] ---> [ Reshape ]
Create more axes Bring axes Merge axes
into correct order
回溯法
最安全的解决方法,给定输入和输出是通过的,可以称之为回溯法,即分割输入的轴(当从较小的 nd
到较大的 nd
) 或拆分输出轴(当从较大 nd
变为较小 nd
时)。拆分的想法是使较小 nd
的暗淡数与较大的 nd
相同。然后,研究输出的步长并将其与输入进行匹配以获得所需的排列顺序。最后,最后可能需要重塑(默认方式或 C 顺序),如果最后一个是较小的 nd
合并轴。
如果输入和输出都具有相同数量的 dims,那么我们将需要将两者拆分并分成块并研究它们相互间的步幅。在这种情况下,我们应该有块大小的额外输入参数,但这可能是题外话。
例子
让我们使用这个具体案例来演示如何应用这些策略。在这里,输入是 4D
,而输出是 2D
。所以,很可能,我们不需要重塑来分裂。所以,我们需要从排列轴开始。因为,最终输出不是 4D
,而是 2D
,我们需要在最后进行整形。
现在,这里的输入是:
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]])
更多示例
我挖掘了我的历史,发现很少有 Q&As
基于 nd
到 nd
的转换。这些可以作为其他示例案例,尽管解释较少(主要是)。如前所述,最多两个 reshapes
和最多一个 swapaxes
/transpose
完成了所有工作。它们如下所示:
,尽管有时我更容易检查 transpose
和 reshape
涵盖的所有可能情况。
例如下面的代码
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
不够通用,无法涵盖我的情况,我必须做一些更复杂的事情。
在出于教学原因实施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
的一些解释将非常有帮助。谢谢!
您似乎在寻找 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]])
为了帮助您理解为什么需要换位,让我们分析您的错误形状输出(通过单个 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 的元素彼此连续,以便后续的重塑调用为您提供所需的输出。
要了解如何正确转置,您应该跟踪输入中的元素并找出需要跳转哪些轴才能到达输出中的每个元素。相应地进行换位。
nd
到 nd
转换的总体思路
这种 nd
到 nd
转换的想法只使用了两件事 -
置换轴(如果只需要交换两个轴,则使用
numpy.transpose
ornumpy.moveaxis
ornumpy.rollaxis
if the needed permute order is a rolled one ornumpy.swapaxes
)和整形。
Permute axes : 获取顺序,使得扁平化版本对应于扁平化版本的输出。所以,如果你不知何故最终使用它两次,再看一遍,因为你不应该。
Reshape : 拆分轴或将最终输出调整为所需的形状。拆分轴主要是在开始时需要的,当输入是 lower-dim 并且我们需要拆分成块时。同样,你不应该需要两次以上。
因此,一般我们会分为三个步骤:
[ Reshape ] ---> [ Permute axes ] ---> [ Reshape ]
Create more axes Bring axes Merge axes
into correct order
回溯法
最安全的解决方法,给定输入和输出是通过的,可以称之为回溯法,即分割输入的轴(当从较小的 nd
到较大的 nd
) 或拆分输出轴(当从较大 nd
变为较小 nd
时)。拆分的想法是使较小 nd
的暗淡数与较大的 nd
相同。然后,研究输出的步长并将其与输入进行匹配以获得所需的排列顺序。最后,最后可能需要重塑(默认方式或 C 顺序),如果最后一个是较小的 nd
合并轴。
如果输入和输出都具有相同数量的 dims,那么我们将需要将两者拆分并分成块并研究它们相互间的步幅。在这种情况下,我们应该有块大小的额外输入参数,但这可能是题外话。
例子
让我们使用这个具体案例来演示如何应用这些策略。在这里,输入是 4D
,而输出是 2D
。所以,很可能,我们不需要重塑来分裂。所以,我们需要从排列轴开始。因为,最终输出不是 4D
,而是 2D
,我们需要在最后进行整形。
现在,这里的输入是:
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]])
更多示例
我挖掘了我的历史,发现很少有 Q&As
基于 nd
到 nd
的转换。这些可以作为其他示例案例,尽管解释较少(主要是)。如前所述,最多两个 reshapes
和最多一个 swapaxes
/transpose
完成了所有工作。它们如下所示:
transpose
和 reshape
涵盖的所有可能情况。
例如下面的代码
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
不够通用,无法涵盖我的情况,我必须做一些更复杂的事情。