Numpy 中的 reshape 操作

关于 numpy 中的 array,改变其 shape,有时可以有时不可以,这很奇怪。

比如

arr = np.zeros((4,4))
slc = arr[:3,:]
slc.shape
(3, 4)
slc.shape = 4,3
slc
array([[0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.],
       [0., 0., 0.]])
slc = arr[:,:3]
slc.shape
(4, 3)
slc.shape = 3,4
---------------------------------------------------------------------------

AttributeError                            Traceback (most recent call last)

<ipython-input-11-780d0b1f4a04> in <module>()
----> 1 slc.shape = 3,4
AttributeError: incompatible shape for a non-contiguous array

这时,slc 无法改变 shape,这主要和 arr 在内存中的存储形式有关,在初始化 arr 的时候,里面的数据就按顺序排好了,而切片取前三列后如果想进行改变形状的操作,就需要在内存中跳跃,这对计算机来说是很困难的。使用 resize 同样无法改变形状

slc.resize(3, 4, refcheck = False)
---------------------------------------------------------------------------

ValueError                                Traceback (most recent call last)

<ipython-input-13-36d7dadd0aa5> in <module>()
----> 1 slc.resize(3, 4, refcheck = False)
ValueError: resize only works on single-segment arrays

给出的提示更加明了 only works on single-segment arrays 就是因为此时数据分段了。可以用 flags 属性查看一下,slc 并不拥有数据,而是引用 base 数组的数据。没有数据且引用的数据在内存中不连续就是无法 resize 的原因。

>>> slc.flags
  C_CONTIGUOUS : False
  F_CONTIGUOUS : False
  OWNDATA : False
  WRITEABLE : True
  ALIGNED : True
  WRITEBACKIFCOPY : False
  UPDATEIFCOPY : False

因为 reshape 创建一个全新的 array,所以无论对什么数组,都能进行 reshape

slc.reshape(3,4)
array([[0., 0., 0., 0.],
       [0., 0., 0., 0.],
       [0., 0., 0., 0.]])

之所以以 view 来举例是因为一般切片会产生不连续的 array(内存存储),而切片产生的正是原 array 的 view,当然获取 view 有很多种方式,比如直接 newarr = arr.view()

NumPy 中有些函数要求 one segment array,比如

>>> import numpy as np
>>> a = np.arange(10)
>>> a
array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
>>> b = a[::2]
>>> b
array([0, 2, 4, 6, 8])

np.sort(b) 因为 b 只是 a 的一个切片 view 而不是 one segment array,所以函数必须先把 b 复制到一块新的内存上再做排序。