在三维数组中重塑数组集

1 投票
4 回答
1240 浏览
提问于 2025-04-30 21:15

为了进行计算,我有一组数组:一个叫“sub”的数组(如下所示),我想把它重新整理成一个像“test”数组那样的格式:

import numpy as np

sub = np.array([[[[ 1.,  1.],
         [ 1.,  1.]],

        [[ 2.,  2.],
         [ 2.,  2.]],

        [[ 3.,  3.],
         [ 3.,  3.]],

        [[ 4.,  4.],
         [ 4.,  4.]]],

       [[[ 5.,  5.],
         [ 5.,  5.]],

        [[ 6.,  6.],
         [ 6.,  6.]],

        [[ 7.,  7.],
         [ 7.,  7.]],

        [[ 8.,  8.],
         [ 8.,  8.]]]])


test=np.array([[[ 1.,  1.,  2., 2.],
        [ 1.,  1.,  2., 2.],
        [ 3.,  3.,  4., 4.],
        [ 3.,  3.,  4., 4.]],

       [[ 5.,  5.,  6., 6.],
        [ 5.,  5.,  6., 6.],
        [ 7.,  7.,  8., 8.],
        [ 7.,  7.,  8., 8.]]]) 

我在一个帖子中找到了一段代码,似乎适合我的情况,但我遇到了一些错误……

k,l,m,n,p =2,2,2,2,2
conc = np.array([np.ones([p,m,n],dtype=int)*i for i in range(k*l)])
test_reshape=np.vstack([np.hstack(sub[i:i+l]) for i in range(0,k*l*p,l)])
暂无标签

4 个回答

1

可以通过一种reshape/swapaxes的小技巧来实现这个功能:

In [92]: sub.reshape(2,2,2,2,2).swapaxes(2,3).reshape(test.shape)
Out[92]: 
array([[[ 1.,  1.,  2.,  2.],
        [ 1.,  1.,  2.,  2.],
        [ 3.,  3.,  4.,  4.],
        [ 3.,  3.,  4.,  4.]],

       [[ 5.,  5.,  6.,  6.],
        [ 5.,  5.,  6.,  6.],
        [ 7.,  7.,  8.,  8.],
        [ 7.,  7.,  8.,  8.]]])

In [94]: np.allclose(sub.reshape(2,2,2,2,2).swapaxes(2,3).reshape(test.shape), test)
Out[94]: True

我承认我不知道怎么生成这种解决方案,可能需要一些猜测。不过,看起来当你想要重新排列数组中的“块”时,有一种方法可以做到:先把数组变成更高的维度,交换一些轴,然后再变回你想要的形状。考虑到sub.shape(2, 4, 2, 2),变成更高维度的意思就是(2, 2, 2, 2, 2)。所以你只需要测试一种形式的解决方案:

sub.reshape(2,2,2,2,2).swapaxes(i,j).reshape(test.shape)

这很简单:

for i,j in IT.combinations(range(5), 2):
    if np.allclose(sub.reshape(2,2,2,2,2).swapaxes(i,j).reshape(test.shape), test):
       print(i,j)

可以揭示出需要交换的正确轴:

(2, 3)
1

也许有一种纯粹使用numpy的方法,但我不太清楚,而且那种方法会用到一些复杂的技巧。下面的解决方案效率没有那么高,因为它使用了Python的for循环(这样会慢一些),不过它能以一种通用的方式得到你的结果,所以不管你的4D数组的实际大小如何,都能适用。

np.vstack( (sub[vol,2*sheet:2*sheet+2].reshape((4,-1)).T for vol in range(2) for sheet in range(2))).reshape((2,4,-1)
1
import numpy as np
sub = np.array(...)
test = np.array([np.hstack((np.vstack(( s[0],s[1] )),
                            np.vstack(( s[2],s[3] )))) for s in sub])
print test

在提问者的例子中,sub的形状是(2,4,2,2),不过上面的代码可以直接用于形状为(n,4,m,m)的数组。如果数组的形状是(n,k,m,m),那么上面的代码也可以根据不同的需求进行调整。

最后我想补充一下,当你查看代码时,你能清楚地看到代码在做什么,这可能会弥补代码在效率方面的一些缺陷(比如,复制数据和重新调整形状的效率差异)。


更好的解决方案(也不是我的 ;-) 以及一些后续思考

我找到了一位叫unutbu的朋友的这个回答(里面有一个更通用解决方案的链接),提问者可以轻松地根据自己的需求进行调整。不过,由于涉及到复杂的形状调整,数据会被复制,因此提问者可能想要比较这两种方法的性能,考虑到“重新调整形状”对程序总运行时间的影响(也就是说,我认为在运行时间为2分钟的情况下,节省0.3秒的努力可能不值得)。

示例交互会话

接下来,数据和过程是直接引用自上述提到的unutbu的回答,最后两条语句是我添加的,用来显示三个ndarrayxyz)的数据缓冲区地址。

In [1]: import numpy as np

In [2]: x = np.arange(16).reshape((4,2,2))

In [3]: y = x.reshape(2,2,2,2).swapaxes(1,2).reshape(4,-1)

In [4]: x
Out[4]: 
array([[[ 0,  1],
        [ 2,  3]],

       [[ 4,  5],
        [ 6,  7]],

       [[ 8,  9],
        [10, 11]],

       [[12, 13],
        [14, 15]]])

In [5]: y
Out[5]: 
array([[ 0,  1,  4,  5],
       [ 2,  3,  6,  7],
       [ 8,  9, 12, 13],
       [10, 11, 14, 15]])

In [6]: z = x.T

In [7]: [a.__array_interface__['data'][0] for a in (x, y, z)]
Out[7]: [46375856, 45578800, 46375856]

In [8]: 
2

这里有一种不同的方法,可以让你交换、切片并把数组整理成你想要的形状:

>>> t = sub.swapaxes(1, 3).T.swapaxes(1, 3)
>>> x = np.c_[t[::2, 0], t[1::2, 0]]
>>> y = np.c_[t[::2, 1], t[1::2, 1]]
>>> np.array((np.r_[x[0], x[1]], np.r_[y[0], y[1]]))

array([[[ 1.,  1.,  2.,  2.],
        [ 1.,  1.,  2.,  2.],
        [ 3.,  3.,  4.,  4.],
        [ 3.,  3.,  4.,  4.]],

       [[ 5.,  5.,  6.,  6.],
        [ 5.,  5.,  6.,  6.],
        [ 7.,  7.,  8.,  8.],
        [ 7.,  7.,  8.,  8.]]])

补充说明: 或者,你也可以先压缩、切片再堆叠:

>>> x = np.c_[sub[:1][:,::2], sub[:1][:,1::2]].squeeze()
>>> y = np.c_[sub[1:][:,::2], sub[1:][:,1::2]].squeeze()
>>> np.array((np.r_[x[0], x[1]], np.r_[y[0], y[1]]))
# the required array

撰写回答