操作 numpy 数组(连接内部子数组)



我有一个操作 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旁边,依此类推......

那么,从06有多"远"呢?或者换句话说,从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.reshapezip相结合可以做你想做的事,但它有点不稳定:

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

最新更新