@mgillson 发现的链接似乎解决了“我如何判断它是否制作了副本”的问题,而不是“我如何预测它”或理解它制作副本的原因。至于测试,我喜欢用A.__array_interfrace__。
如果您尝试将值分配给重整后的数组,并希望同时更改原始数组,这很可能会成为问题。而且我很难找到有问题的 SO 案例。
复制重塑会比非复制重塑慢一点,但我再次想不出会导致整个代码变慢的情况。如果您使用的数组太大以至于最简单的操作会产生内存错误,那么副本也可能是一个问题。
在重塑数据缓冲区中的值后,需要以连续的顺序排列,“C”或“F”。例如:
In [403]: np.arange(12).reshape(3,4,order='C')
Out[403]:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
In [404]: np.arange(12).reshape(3,4,order='F')
Out[404]:
array([[ 0, 3, 6, 9],
[ 1, 4, 7, 10],
[ 2, 5, 8, 11]])
如果初始订单非常“混乱”以至于无法返回这样的值,它将进行复制。转置后重塑可能会这样做(请参阅下面的示例)。 stride_tricks.as_strided 的游戏也可能如此。我能想到的就只有这些了。
In [405]: x=np.arange(12).reshape(3,4,order='C')
In [406]: y=x.T
In [407]: x.__array_interface__
Out[407]:
{'version': 3,
'descr': [('', '<i4')],
'strides': None,
'typestr': '<i4',
'shape': (3, 4),
'data': (175066576, False)}
In [408]: y.__array_interface__
Out[408]:
{'version': 3,
'descr': [('', '<i4')],
'strides': (4, 16),
'typestr': '<i4',
'shape': (4, 3),
'data': (175066576, False)}
y,转置,具有相同的“数据”指针。转置是在不更改或复制数据的情况下执行的,它只是使用新的shape、strides 和flags 创建了一个新对象。
In [409]: y.flags
Out[409]:
C_CONTIGUOUS : False
F_CONTIGUOUS : True
...
In [410]: x.flags
Out[410]:
C_CONTIGUOUS : True
F_CONTIGUOUS : False
...
y 是订单“F”。现在尝试重塑它
In [411]: y.shape
Out[411]: (4, 3)
In [412]: z=y.reshape(3,4)
In [413]: z.__array_interface__
Out[413]:
{...
'shape': (3, 4),
'data': (176079064, False)}
In [414]: z
Out[414]:
array([[ 0, 4, 8, 1],
[ 5, 9, 2, 6],
[10, 3, 7, 11]])
z 是一个副本,它的data 缓冲区指针不同。它的值没有以任何类似于x 或y 的方式排列,没有0,1,2,...。
但简单地重塑 x 并不会产生副本:
In [416]: w=x.reshape(4,3)
In [417]: w
Out[417]:
array([[ 0, 1, 2],
[ 3, 4, 5],
[ 6, 7, 8],
[ 9, 10, 11]])
In [418]: w.__array_interface__
Out[418]:
{...
'shape': (4, 3),
'data': (175066576, False)}
拉开y和y.reshape(-1)一样;它作为副本生成:
In [425]: y.reshape(-1)
Out[425]: array([ 0, 4, 8, 1, 5, 9, 2, 6, 10, 3, 7, 11])
In [426]: y.ravel().__array_interface__['data']
Out[426]: (175352024, False)
像这样将值分配给一个 raveled 数组可能是最有可能的情况,其中副本会产生错误。例如,x.ravel()[::2]=99 每隔一个值更改x 和y(分别为列和行)。但是y.ravel()[::2]=0 因为这个抄袭什么都没做。
所以转置后重塑是最有可能的复制方案。我很乐意探索其他可能性。
编辑: y.reshape(-1,order='F')[::2]=0 确实改变了 y 的值。使用兼容的顺序,reshape 不会生成副本。
@mgillson 链接中的一个答案https://stackoverflow.com/a/14271298/901925 指出A.shape=... 语法可以防止复制。如果不复制就不能改变形状会报错:
In [441]: y.shape=(3,4)
...
AttributeError: incompatible shape for a non-contiguous array
reshape 文档中也提到了这一点
如果您希望在复制数据时引发错误,
您应该将新形状分配给数组的形状属性::
关于重塑以下as_strided的问题:
reshaping a view of a n-dimensional array without using reshape
和
Numpy View Reshape Without Copy (2d Moving/Sliding Window, Strides, Masked Memory Structures)
===========================
这是我将shape.c/_attempt_nocopy_reshape 翻译成Python 的第一个步骤。它可以通过以下方式运行:
newstrides = attempt_reshape(numpy.zeros((3,4)), (4,3), False)
import numpy # there's an np variable in the code
def attempt_reshape(self, newdims, is_f_order):
newnd = len(newdims)
newstrides = numpy.zeros(newnd+1).tolist() # +1 is a fudge
self = numpy.squeeze(self)
olddims = self.shape
oldnd = self.ndim
oldstrides = self.strides
#/* oi to oj and ni to nj give the axis ranges currently worked with */
oi,oj = 0,1
ni,nj = 0,1
while (ni < newnd) and (oi < oldnd):
print(oi, ni)
np = newdims[ni];
op = olddims[oi];
while (np != op):
if (np < op):
# /* Misses trailing 1s, these are handled later */
np *= newdims[nj];
nj += 1
else:
op *= olddims[oj];
oj += 1
print(ni,oi,np,op,nj,oj)
#/* Check whether the original axes can be combined */
for ok in range(oi, oj-1):
if (is_f_order) :
if (oldstrides[ok+1] != olddims[ok]*oldstrides[ok]):
# /* not contiguous enough */
return 0;
else:
#/* C order */
if (oldstrides[ok] != olddims[ok+1]*oldstrides[ok+1]) :
#/* not contiguous enough */
return 0;
# /* Calculate new strides for all axes currently worked with */
if (is_f_order) :
newstrides[ni] = oldstrides[oi];
for nk in range(ni+1,nj):
newstrides[nk] = newstrides[nk - 1]*newdims[nk - 1];
else:
#/* C order */
newstrides[nj - 1] = oldstrides[oj - 1];
#for (nk = nj - 1; nk > ni; nk--) {
for nk in range(nj-1, ni, -1):
newstrides[nk - 1] = newstrides[nk]*newdims[nk];
nj += 1; ni = nj
oj += 1; oi = oj
print(olddims, newdims)
print(oldstrides, newstrides)
# * Set strides corresponding to trailing 1s of the new shape.
if (ni >= 1) :
print(newstrides, ni)
last_stride = newstrides[ni - 1];
else :
last_stride = self.itemsize # PyArray_ITEMSIZE(self);
if (is_f_order) :
last_stride *= newdims[ni - 1];
for nk in range(ni, newnd):
newstrides[nk] = last_stride;
return newstrides