Python中reshape时使用ravel和transpose有什么区别?

5

我有一个二维数组vv.shape=(M_1,M_2),我想将其重新整形为一个三维数组,其维度为v.shape=(M_2,N_1,N_2),且M_1=N_1*N_2

我想到了以下代码行,可以得到相同的结果:

np.reshape(v.T, reshape_tuple)

并且

np.reshape(v.ravel(order='F'), reshape_tuple)

对于 reshape_tuple=(M_2,N_1,N_2),从计算角度来说哪种更好(计算时间、内存等方面),如果原始的 v 是一个巨大的(可能是复数值)矩阵?

我猜使用转置更好,但如果 reshape 做了自动的 ravel,那么 ravel 选项可能更快(尽管 reshape 可能会在 C 或 Fortran 中执行 ravel,这就不明确了)?

1个回答

4

它们所做的事情的顺序-重塑、改变步幅和复制-不同,但它们最终都会做同样的事情。

我喜欢使用__array_interface__来查看数据缓冲区的位置和其他更改。我想我应该添加flags来查看order。但是我们/您已经知道transpose将顺序更改为F了,对吧?

In [549]: x=np.arange(6).reshape(2,3)
In [550]: x.__array_interface__
Out[550]: 
{'data': (187732024, False),
 'descr': [('', '<i4')],
 'shape': (2, 3),
 'strides': None,
 'typestr': '<i4',
 'version': 3}

转置是一种视图,它具有不同的形状、步幅和顺序:

In [551]: x.T.__array_interface__
Out[551]: 
{'data': (187732024, False),
 'descr': [('', '<i4')],
 'shape': (3, 2),
 'strides': (4, 12),
 'typestr': '<i4',
 'version': 3}

以不同顺序旅行是一份拷贝(具有不同的数据缓冲指针)。
In [552]: x.ravel(order='F').__array_interface__
Out[552]: 
{'data': (182286992, False),
 'descr': [('', '<i4')],
 'shape': (6,),
 'strides': None,
 'typestr': '<i4',
 'version': 3}

转置(transpose)和拉平(ravel)也都是副本。我认为使用相同的数据指针只是内存重用的一种情况(因为我没有给变量赋值),但可以进行检查。

In [553]: x.T.ravel().__array_interface__
Out[553]: 
{'data': (182286992, False),
 'descr': [('', '<i4')],
 'shape': (6,),
 'strides': None,
 'typestr': '<i4',
 'version': 3}

添加reshape:

In [554]: x.T.ravel().reshape(2,3).__array_interface__
Out[554]: 
{'data': (182286992, False),
 'descr': [('', '<i4')],
 'shape': (2, 3),
 'strides': None,
 'typestr': '<i4',
 'version': 3}
In [555]: x.ravel(order='F').reshape(2,3).__array_interface__
Out[555]: 
{'data': (182286992, False),
 'descr': [('', '<i4')],
 'shape': (2, 3),
 'strides': None,
 'typestr': '<i4',
 'version': 3}

我认为在reshape中有一个隐含的“ravel”:

In [558]: x.T.reshape(2,3).__array_interface__
Out[558]: 
{'data': (182286992, False),
 'descr': [('', '<i4')],
 'shape': (2, 3),
 'strides': None,
 'typestr': '<i4',
 'version': 3}

(我应该重新修改这些示例,以消除内存重用的歧义。)无论哪种情况,在转置后进行重塑都需要进行与更改顺序的ravel相同的内存复制。据我所知,仅需要一次复制以进行任何一种情况。其他操作只涉及形状等属性的更改。

如果我们只看数组,则可能更清楚。

In [565]: x.T
Out[565]: 
array([[0, 3],
       [1, 4],
       [2, 5]])

在重塑(reshape)之前,我们仍然可以按数字顺序遍历数组中的T。 但是,在重塑之后,10相距甚远,显然发生了复制。
In [566]: x.T.reshape(2,3)
Out[566]: 
array([[0, 3, 1],
       [4, 2, 5]])

在 ravel 操作之后,值的顺序看起来相似,经过 reshape 操作后更加明显。
In [567]: x.ravel(order='F')
Out[567]: array([0, 3, 1, 4, 2, 5])
In [568]: x.ravel(order='F').reshape(2,3)
Out[568]: 
array([[0, 3, 1],
       [4, 2, 5]])

好的,我明白了,看起来选择什么并不重要。谢谢! - python_freak
有趣的是,时间表明np.reshape(v.T, reshape_tuple)比np.reshape(v.ravel(order='F'), reshape_tuple)快得多。 - python_freak
也许 reshape_tuple 是这样的,即 np.reshape(v.T, reshape_tuple) 不需要复制。也就是说,新形状与转置形状兼容。我会检查 __array_interface__ - hpaulj

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