将numpy数组的所有维度折叠成前两个维度,除了第一和第二个维度。

3

我是一个能翻译文本的助手。

以下是需要翻译的内容:

我有一个变量维度的numpy数组,例如它可能具有以下形状

(64, 64)
(64, 64, 2, 5)
(64, 64, 40)
(64, 64, 10, 20, 4)

我想做的是,如果维度数大于3,我希望将其他所有维度都折叠/堆叠到第三个维度中,同时保持顺序。因此,在上面的例子中,操作后的形状应该是:

(64, 64)
(64, 64, 10)
(64, 64, 40)
(64, 64, 800)

此外,顺序需要被保留。例如,形状为(64, 64, 2, 5)的数组应该按照以下方式堆叠:
(64, 64, 2)
(64, 64, 2)
(64, 64, 2)
(64, 64, 2)
(64, 64, 2)

例如,一个接一个地显示3D切片。此外,在操作之后,我希望将其重新塑造回原始形状而不进行任何置换,即保留原始顺序。

我可以采取的一种方法是将从3到最后一个维度的所有维度值相乘,即:

shape = array.shape
if len(shape) > 3:
    final_dim = 1
    for i in range(2, len(shape)):
        final_dim *= shape[i]

然后重新塑造数组。类似于:

array.reshape(64, 64, final_dim)

然而,我首先不确定顺序是否按照我的要求保留,并且是否有更好的Pythonic方式来实现这个?


你能否澄清一下堆叠的问题,例如对于样本 a(64, 64, 2, 5),在输出数组 out 中应该将 a[:,:,:,0] 放置在哪里? - Divakar
请注意,在 numpy 中,最后一个维度是最内部的维度。如果您在 (N,2,3) 数组上进行迭代,则将使用 N 个 (2,3) 数组。MATLAB 使用相反的顺序;第一个是最内部的,最后一个是最外部的。这两种语言显示 3D 数组的方式都反映了这种顺序。 - hpaulj
4个回答

4

编辑:正如其他答案中指出的那样,只需在reshape中提供-1作为第三个维度即可更轻松地确定正确的形状。Numpy会自动确定正确的形状。

我不确定这里的问题是什么。您可以直接使用np.reshape,它会保持顺序。请参阅以下代码:

import numpy as np

A = np.random.rand(20,20,2,2,18,5)
print A.shape

new_dim = np.prod(A.shape[2:])
print new_dim
B = np.reshape(A, (A.shape[0], A.shape[1], np.prod(A.shape[2:])))
print B.shape

C = B.reshape((20,20,2,2,18,5))
print np.array_equal(A,C)

输出结果如下:
(20L, 20L, 2L, 2L, 18L, 5L)
360
(20L, 20L, 360L)
True

这完全实现了你所要求的。

2
尝试使用.reshape(64, 64, -1); 关于reshape中的-1,请参见最近的这个问题: https://dev59.com/21gR5IYBdhLWcg3wEZ2V - hpaulj
非常好。我在脑海中想到了这样的东西,但是记不起来了。 :) - Martin Krämer

2

重新塑形接受自动重新调整尺寸:

a=rand(20,20,8,6,4)
s=a.shape[:2]
if a.ndim>2 : s = s+ (-1,)
b=a.reshape(s)

这太棒了。谢谢! - Luca

0

根据给定的 (64, 64, 2, 5) 样本的堆叠要求,我认为您需要对轴进行置换。对于置换,我们可以使用 np.rollaxis,如下所示 -

def collapse_dims(a):
    if a.ndim>3:
        return np.rollaxis(a,-1,2).reshape(a.shape[0],a.shape[1],-1)
    else:
        return a

给定四种样本形状的示例运行 -

1)样本形状:

In [234]: shp1 = (64, 64)
     ...: shp2 = (64, 64, 2, 5)
     ...: shp3 = (64, 64, 40)
     ...: shp4 = (64, 64, 10, 20, 4)
     ...: 

案例 #1:

In [235]: a = np.random.randint(11,99,(shp1))

In [236]: np.allclose(a, collapse_dims(a))
Out[236]: True

案例 #2 :

In [237]: a = np.random.randint(11,99,(shp2))

In [238]: np.allclose(a[:,:,:,0], collapse_dims(a)[:,:,0:2])
Out[238]: True

In [239]: np.allclose(a[:,:,:,1], collapse_dims(a)[:,:,2:4])
Out[239]: True

In [240]: np.allclose(a[:,:,:,2], collapse_dims(a)[:,:,4:6]) # .. so on
Out[240]: True

案例 #3 :

In [241]: a = np.random.randint(11,99,(shp3))

In [242]: np.allclose(a, collapse_dims(a))
Out[242]: True

案例 #4:

In [243]: a = np.random.randint(11,99,(shp4))

In [244]: np.allclose(a[:,:,:,:,0].ravel(), collapse_dims(a)[:,:,:200].ravel())
Out[244]: True

In [245]: np.allclose(a[:,:,:,:,1].ravel(), collapse_dims(a)[:,:,200:400].ravel())
Out[245]: True

0

我会尝试阐述@Divaker提出的问题。

In [522]: arr = np.arange(2*2*3*4).reshape(2,2,3,4)
In [523]: arr
Out[523]: 
array([[[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7],
         [ 8,  9, 10, 11]],

        [[12, 13, 14, 15],
         [16, 17, 18, 19],
         [20, 21, 22, 23]]],


       [[[24, 25, 26, 27],
         [28, 29, 30, 31],
         [32, 33, 34, 35]],

        [[36, 37, 38, 39],
         [40, 41, 42, 43],
         [44, 45, 46, 47]]]])

4是最内层的维度,因此它将数组显示为3x4个块。如果您注意空格和[],则会看到有2x2个块。

当我们使用reshape时,请注意发生了什么:

In [524]: arr1 = arr.reshape(2,2,-1)
In [525]: arr1
Out[525]: 
array([[[ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11],
        [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23]],

       [[24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35],
        [36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47]]])

现在它是2个2x12块。您可以对这些12个元素行进行任何操作,并将它们重新塑形为3x4块。

In [526]: arr1.reshape(2,2,3,4)
Out[526]: 
array([[[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7],
      ...

但我也可以在最后一个维度上分割这个数组。np.split可以做到,但列表推导更容易理解:

In [527]: alist = [arr[...,i] for i in range(4)]
In [528]: alist
Out[528]: 
[array([[[ 0,  4,  8],
         [12, 16, 20]],

        [[24, 28, 32],
         [36, 40, 44]]]), 
 array([[[ 1,  5,  9],
         [13, 17, 21]],

        [[25, 29, 33],
         [37, 41, 45]]]), 
 array([[[ 2,  6, 10],
         [14, 18, 22]],

        [[26, 30, 34],
         [38, 42, 46]]]), 
 array([[[ 3,  7, 11],
         [15, 19, 23]],

        [[27, 31, 35],
         [39, 43, 47]]])]

这个包含4个(2,2,3)的数组。请注意,3个元素的行在4d显示中显示为列。

我可以使用np.stack将其重组成一个4d数组(类似于np.array,但可以更好地控制如何连接数组):

In [529]: np.stack(alist, axis=-1)
Out[529]: 
array([[[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7],
         [ 8,  9, 10, 11]],
         ...
        [[36, 37, 38, 39],
         [40, 41, 42, 43],
         [44, 45, 46, 47]]]])

==========

split 的等效方法是 [x[...,0] for x in np.split(arr, 4, axis=-1)]。没有索引的 split 会产生 (2, 2, 3, 1) 的数组。

collapse_dims 会产生以下结果(以我的示例为例):

In [532]: np.rollaxis(arr,-1,2).reshape(arr.shape[0],arr.shape[1],-1)
Out[532]: 
array([[[ 0,  4,  8,  1,  5,  9,  2,  6, 10,  3,  7, 11],
        [12, 16, 20, 13, 17, 21, 14, 18, 22, 15, 19, 23]],

       [[24, 28, 32, 25, 29, 33, 26, 30, 34, 27, 31, 35],
        [36, 40, 44, 37, 41, 45, 38, 42, 46, 39, 43, 47]]])

一个 (2,2,12) 的数组,但是行内元素的顺序不同。在展开之前,它会对内部的两个维度进行转置。
In [535]: arr[0,0,:,:].ravel()
Out[535]: array([ 0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11])
In [536]: arr[0,0,:,:].T.ravel()
Out[536]: array([ 0,  4,  8,  1,  5,  9,  2,  6, 10,  3,  7, 11])

将其恢复到原始顺序需要另一个滚动或转置

In [542]: arr2.reshape(2,2,4,3).transpose(0,1,3,2)
Out[542]: 
array([[[[ 0,  1,  2,  3],
         [ 4,  5,  6,  7],
         [ 8,  9, 10, 11]],

      ....

        [[36, 37, 38, 39],
         [40, 41, 42, 43],
         [44, 45, 46, 47]]]])

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接