我有一个操作 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)
阵列
a[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,:,:,:,:]
)遵循相同的模式。
您的主题行回答了您的问题:
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
有效地破坏数组,然后应用新形状:
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])
首先交换轴,使用 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(a, (1, 0, 2))
一样使用 np.transpose 将轴从(0, 1, 2)
交换到(1, 0, 2)
,正如 MadPhysicist 所指出的那样。
我认为在这种情况下(第一个例子),简单地说:
>>> a.swapaxes(0, 1).reshape(2, -1)
array([[1, 2, 5, 6],
[3, 4, 7, 8]])
一般来说,我发现@Divakar迷你教程是此类操作的权威来源。
编辑更新问题后(以包含具有更大数组的示例),我为此类问题编写了一个小型求解器(实际上非常快)。
以下任何一项都会产生相同的结果,该结果满足约束:
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)
# ...
当然,您可以决定通过-1
更改.reshape()
中的任何单个值,以"使其更通用"或更直观。例如:
np.rollaxis(a.reshape(2, 3, 3, 2, -1), 0, 3)
在新的更新中,您可以使用 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 步幅来实现这一目标。让我们检查一下q
的步伐:
>>> q.strides
(12, 4, 4)
在我们的输出中,除了第三个步幅之外,所有步幅都应该保持不变,因为在第三维中我们需要与q
下半部分的值堆叠,即:6
放在0
旁边,7
放在1
旁边,依此类推......
那么,从0
到6
有多"远"呢?或者换句话说,从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)
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
有效地做到了这一点。
您可以使用 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]]