如何将numpy数组中的部分维度展平

241
有没有一种快速的方法可以“子扁平化”或仅扁平化numpy数组中的前几个维度?
例如,给定一个尺寸为(50,100,25)的numpy数组,结果尺寸将为(5000,25)

1
这可能会有所帮助 https://dev59.com/RGYr5IYBdhLWcg3wDGHP - Ankur Ankan
1
你需要一个关于numpy ndarray数组切片的复习课程。也被称为多维数组索引,请参见:https://docs.scipy.org/doc/numpy-1.13.0/reference/arrays.indexing.html 使用方括号对ndarray进行数组切片,并使用逗号分隔符来分隔每个维度的数量。它看起来像这样(不完全相同):your_array[50:100, 7, :],这将把3D对象压缩成2D,仅使用第二维的切片编号7。 - Eric Leschinski
2
切片只是取一个子集,海报想要保留所有数据点。我猜你的意思是 array[0:50,7,:],这将给出大小为 (50,25) 的结果,舍弃了99%的数据。 - Sherman
5个回答

215

请参考numpy.reshape

>>> arr = numpy.zeros((50,100,25))
>>> arr.shape
# (50, 100, 25)

>>> new_arr = arr.reshape(5000,25)
>>> new_arr.shape   
# (5000, 25)

# One shape dimension can be -1. 
# In this case, the value is inferred from 
# the length of the array and remaining dimensions.
>>> another_arr = arr.reshape(-1, arr.shape[-1])
>>> another_arr.shape
# (5000, 25)

50
对我来说,这些解决方案似乎有点不太优雅,因为它们需要一些冗余的信息。我希望有一种方法可以只需要指定子集维度,比如 arr.flatten(dimensions=(0, 1)) - Denziloe
5
在不指定额外数据将折叠到哪个维度的情况下,无法简单地“展平”ndarray的任意维度。以一个2x2x3的ndarray为例,展平最后一个维度可以产生一个2x6或6x2的数组,所以信息不是多余的。您可以使用-1来指定维度:从numpy.reshape中得知,其中一个形状维度可以是-1。在这种情况下,该值是从数组的长度和剩余维度推断出来的。因此,将2x2xN重塑为2Nx2的数组看起来像这样:arr.reshape((-1,2)) - אלימלך שרייבר
2
@Denziloe 一种实现这个的方法可能是 arr.reshape(arr.shape[0] * arr.shape[1], arr.shape[2]) - Adrien Pavao
5
有趣的是,torch 似乎可以通过 https://pytorch.org/docs/stable/generated/torch.flatten.html 来管理这个问题 ;) - Sebastian Hoffmann
1
@SebastianHoffmann,numpy的flatten也可以处理。如函数名称所示,Flatten将张量/ndarray压缩为1-D数组,因此*无需解决任何歧义。 而在这里讨论的问题是将单个维度(例如6-D)压缩为5-D张量/ndarray。 Torch Reshape在这方面需要相同的规格说明。 - אלימלך שרייבר
显示剩余3条评论

119

对 Alexander 回答的轻微概括 - np.reshape 可以将 -1 作为参数,表示“总数组大小除以所有其他列出的维度的乘积”:

例如,要使除最后一维之外的所有维度均展开:

>>> arr = numpy.zeros((50,100,25))
>>> new_arr = arr.reshape(-1, arr.shape[-1])
>>> new_arr.shape
# (5000, 25)

74

对 Peter 回答的略微概括 -- 如果你想超越三维数组,可以在原始数组的形状上指定一个范围。

例如,要展开除最后两个维度之外的所有维度:

arr = numpy.zeros((3, 4, 5, 6))
new_arr = arr.reshape(-1, *arr.shape[-2:])
new_arr.shape
# (12, 5, 6)

编辑:对我之前的回答稍作概括——在重塑的开始处也可以指定范围:

arr = numpy.zeros((3, 4, 5, 6, 7, 8))
new_arr = arr.reshape(*arr.shape[:2], -1, *arr.shape[-2:])
new_arr.shape
# (3, 4, 30, 7, 8)

37
已经过去两年了……我们需要再做一些小的概括!;) - Lith

17

numpy.vstack非常适合这种情况。

import numpy as np
arr = np.ones((50,100,25))
np.vstack(arr).shape
> (5000, 25)

我更喜欢使用 stackvstack 或者 hstack 而不是 reshape,因为 reshape 只是简单地扫描数据,并将其强制转换成所需的形状。如果你要计算列平均值,这可能会导致问题。

以下是一个例子来说明我的观点。假设我们有以下数组:

>>> arr.shape
(2, 3, 4)
>>> arr 
array([[[1, 2, 3, 4],
        [1, 2, 3, 4],
        [1, 2, 3, 4]],

       [[7, 7, 7, 7],
        [7, 7, 7, 7],
        [7, 7, 7, 7]]])


我们使用两种方法来获得一个形状为(3,8)的数组。
>>> arr.reshape((3,8)).shape
(3, 8)
>>> np.hstack(arr).shape 
(3, 8)

然而,如果我们看一下它们在每种情况下是如何被重塑的,hstack 可以让我们获取列求和结果,这也可以从原始数组中计算得出。使用reshape就不可能实现这一点。
>>> arr.reshape((3,8))
array([[1, 2, 3, 4, 1, 2, 3, 4],
       [1, 2, 3, 4, 7, 7, 7, 7],
       [7, 7, 7, 7, 7, 7, 7, 7]])
>>> np.hstack(arr)
array([[1, 2, 3, 4, 7, 7, 7, 7],
       [1, 2, 3, 4, 7, 7, 7, 7],
       [1, 2, 3, 4, 7, 7, 7, 7]])

6

另一种方法是使用numpy.resize(),示例代码如下:

In [37]: shp = (50,100,25)
In [38]: arr = np.random.random_sample(shp)
In [45]: resized_arr = np.resize(arr, (np.prod(shp[:2]), shp[-1]))
In [46]: resized_arr.shape
Out[46]: (5000, 25)

# sanity check with other solutions
In [47]: resized = np.reshape(arr, (-1, shp[-1]))
In [48]: np.allclose(resized_arr, resized)
Out[48]: True

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