Numpy:使用reshape或newaxis添加维度

27

可以使用ndarray.reshapenumpy.newaxis来向数组添加一个新的维度。它们似乎都会创建一个视图,使用其中一个而不是另一个是否有任何原因或优势?

>>> b
array([ 1.,  1.,  1.,  1.])
>>> c = b.reshape((1,4))
>>> c *= 2
>>> c
array([[ 2.,  2.,  2.,  2.]])
>>> c.shape
(1, 4)
>>> b
array([ 2.,  2.,  2.,  2.])
>>> d = b[np.newaxis,...]
>>> d
array([[ 2.,  2.,  2.,  2.]])
>>> d.shape
(1, 4)
>>> d *= 2
>>> b
array([ 4.,  4.,  4.,  4.])
>>> c
array([[ 4.,  4.,  4.,  4.]])
>>> d
array([[ 4.,  4.,  4.,  4.]])
>>> 

`


3
b.reshape(1,-1) 是一个方便的快捷方式。它将数组 b 转换为一行,列数自动确定。 - hpaulj
2个回答

31

使用numpy.newaxis而不是ndarray.reshape的原因之一是当您有多个“未知”维度需要操作时。例如,对于以下数组:

>>> arr.shape
(10, 5)

这个有效:

>>> arr[:, np.newaxis, :].shape
(10, 1, 5)

但这个不会:

>>> arr.reshape(-1, 1, -1)
...
ValueError: can only specify one unknown dimension

25

我没有看到太大的差异证据。您可以对非常大的数组进行时间测试。基本上,两者都可以更改形状,可能还会更改步幅。 __array_interface__ 是访问此信息的好方法。例如:

In [94]: b.__array_interface__
Out[94]: 
{'data': (162400368, False),
 'descr': [('', '<f8')],
 'shape': (5,),
 'strides': None,
 'typestr': '<f8',
 'version': 3}

In [95]: b[None,:].__array_interface__
Out[95]: 
{'data': (162400368, False),
 'descr': [('', '<f8')],
 'shape': (1, 5),
 'strides': (0, 8),
 'typestr': '<f8',
 'version': 3}

In [96]: b.reshape(1,5).__array_interface__
Out[96]: 
{'data': (162400368, False),
 'descr': [('', '<f8')],
 'shape': (1, 5),
 'strides': None,
 'typestr': '<f8',
 'version': 3}

两者都使用相同的data缓冲区创建一个视图。形状相同,但重新整形不会改变stridesreshape允许您指定order

.flags显示C_CONTIGUOUS标志的差异。

reshape可能更快,因为它进行的更改较少。但无论哪种方式,该操作都不应显著影响更大计算的时间。

例如,对于大型的b

In [123]: timeit np.outer(b.reshape(1,-1),b)
1 loops, best of 3: 288 ms per loop
In [124]: timeit np.outer(b[None,:],b)
1 loops, best of 3: 287 ms per loop

有趣的观察结果: b.reshape(1,4).strides -> (32, 8)

我的猜测是,.__array_interface__ 显示了一个底层属性,而 .strides 更像是一个属性(尽管它可能全部埋在 C 代码中)。默认的底层值为 None,当需要进行计算(或者使用 .strides 进行显示)时,它会从形状和项目大小计算得出。 32 是第一行末尾的距离(4x8)。np.ones((2,4)).strides(32,8) 相同(而在__array_interface__ 中是 None)。

另一方面,b[None,:] 则准备进行广播数组。在广播时,现有的值被重复使用。这就是 (0,8) 中的 0 的作用。

In [147]: b1=np.broadcast_arrays(b,np.zeros((2,1)))[0]

In [148]: b1.shape
Out[148]: (2, 5000)

In [149]: b1.strides
Out[149]: (0, 8)

In [150]: b1.__array_interface__
Out[150]: 
{'data': (3023336880L, False),
 'descr': [('', '<f8')],
 'shape': (2, 5),
 'strides': (0, 8),
 'typestr': '<f8',
 'version': 3}

b1np.ones((2,5)) 显示相同,但只有 5 个元素。

np.broadcast_arrays 是位于 /numpy/lib/stride_tricks.py 中的一个函数。它使用同一文件中的 as_strided 函数。这些函数直接操作形状和跨度属性。


酷,...__array_interface__。!! - wwii
哦,b.reshape(1,4).strides -> (32, 8)b[None,...].strides ->(0, 8) - wwii
有趣。我已经加入了一些想法。 - hpaulj
“...为广播准备数组”听起来是正确的。因此,第一个步幅维度/值的零有点强制它在广播期间从开头开始。这可能也解释了C_CONTIGUOUS的差异。 - wwii

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