a.transpose()。ravel()[0] = x无法更改numpy中原始数组的值?

时间:2019-01-13 04:44:30

标签: python numpy deep-copy

环境:Python 3.6.0 | Anaconda自定义(64位),numpy版本:1.11.3
示例:

In[1]: import numpy as np
In[2]: a = np.array([[1,2,3], [4,5,6]])
In[3]: a
Out[4]: 
array([[1, 2, 3],
       [4, 5, 6]])
In[5]: a.transpose()[0] = -1
In[6]: a
Out[6]: 
array([[-1,  2,  3],
       [-1,  5,  6]])
In[7]: a.ravel()[0] = -2 
In[8]: a
Out[8]: 
array([[-2,  2,  3],
       [-1,  5,  6]])
In[9]: a.transpose().ravel()[0] = -3
In[10]: a
Out[10]: 
array([[-2,  2,  3],
       [-1,  5,  6]])

我知道transpose()ravel()返回数组的视图,因此我们可以更改其原始数组的值。但是,当我们使用transpose().ravel()时,我们不能更改它吗?为什么?

2 个答案:

答案 0 :(得分:2)

ravel返回的是副本,而不是视图

来自numpy.ravel docs

  

返回包含输入元素的一维数组。仅在需要时进行复制。

因此,基本上,当破坏移调时,实际上需要一个副本。您正在更改副本中的值,因此不会反映在原始数组中。

测试返回的数组是视图还是副本

对于像这样的简单情况,您可以通过比较ba的身份来测试数组b.base是否为a的视图:

a = np.array([[1,2,3], [4,5,6]])
b = a.T
c = b.ravel()

print('b is a view of a\n%s\n' % (b.base is a))
print('c is a view of a\n%s\n' % (c.base is a))

输出:

b is a view of a
True

c is a view of a
False

为什么a.T.ravel()返回副本?

Shocker:实际上,有一种方法可以使a.T.ravel()返回视图而不是副本。您可以通过显式设置order='F'(即Fortran顺序)来实现:

a = np.array([[1,2,3], [4,5,6]])
c = a.T.ravel()
d = a.T.ravel(order='F')

print('d is a view of a\n%s\n' % (d.base is a))

输出:

d is a view of a
True

但是,更改order kwarg的值将更改raveled数组中的值的顺序(看中的样子):

print('c\n%s\n' % c)
print('d\n%s\n' % d)

输出:

c
[1 4 2 5 3 6]

d
[1 2 3 4 5 6]

为了理解为什么order中的更改导致返回视图,我们可以查看ravel函数本身的代码。 np.ndarray.ravel的实现是buried in the C layer。仔细阅读源代码,很明显,为了从ravel返回视图,必须满足两个条件:

  • 输入数组必须是连续的。

  • 连续输入数组的顺序必须与传递到order的{​​{1}} kwarg的顺序匹配。

该kwarg的默认值为ravel。因此,默认情况下,order='C'仅在C连续数组上运行时才返回视图。在大多数情况下,初始化新的Numpy数组ravel时,它会以C连续的形式开始。但是,转置a将是F连续的。通过检查数组的.flags property,您可以在代码中看到这一点:

a.T

输出:

a = np.array([[1,2,3], [4,5,6]])

print('the flags of a\n%s\n' % a.flags)
print('the flags of a.T\n%s\n' % a.T.flags)

C和F连续到底意味着什么?

对于您来说,C连续和F连续这两个词很有可能看起来很乱。对它们进行解释将需要另外一个完整的问题,很高兴SO上的某个人已经提出过。 a link to an old answer给出了C和F顺序实际含义的非常直观的概述。

注意

在您的实际代码中,我不必担心the flags of a C_CONTIGUOUS : True F_CONTIGUOUS : False OWNDATA : True WRITEABLE : True ALIGNED : True WRITEBACKIFCOPY : False UPDATEIFCOPY : False the flags of a.T C_CONTIGUOUS : False F_CONTIGUOUS : True OWNDATA : False WRITEABLE : True ALIGNED : True WRITEBACKIFCOPY : False UPDATEIFCOPY : False 是返回视图还是副本。实际上,通过确保使用视图并不能始终提高性能。总体上避免过早优化。

答案 1 :(得分:0)

In [382]: a = np.array([[1,2,3], [4,5,6]])
In [383]: a
Out[383]: 
array([[1, 2, 3],
       [4, 5, 6]])
In [384]: a.ravel()
Out[384]: array([1, 2, 3, 4, 5, 6])

ravel提供了该数组的一维视图-并按照它们在数据缓冲区中出现的顺序显示了这些值。

In [385]: a.T
Out[385]: 
array([[1, 4],
       [2, 5],
       [3, 6]])
In [386]: a.T.ravel()
Out[386]: array([1, 4, 2, 5, 3, 6])
转置的

ravel以不同的顺序显示元素-除非我们将顺序指定为“ F”(或“ K”)。

In [387]: a.T.ravel(order='F')
Out[387]: array([1, 2, 3, 4, 5, 6])
如果数组可以使用原始数据,并且仅更改ravelview,则

shape(和其他操作)将生成strides。如果不能,则必须进行复制。

由于元素因转置而发生了变化,因此用[0]以外的索引编制索引,因此选择了另一个值:

In [397]: a.ravel()[3]
Out[397]: 4               # -1 in your Out[8]
In [398]: a.T.ravel()[3]
Out[398]: 5

当您请求更改移调的第4个元素时,您会发现有歧义。视您遍历元素的方式而定。