系列文章
一次性搞定NumPy入门基础知识
NumPy之操控ndarray的形状
NumPy之浅拷贝和深拷贝
NumPy之索引技巧
概述
NumPy经常会操作size很大的数据结构,如果不加小心,会产生很大的内存和性能浪费,因此要理解操作中的各种行为,根据实际情况选择最合理的方法。
完全没有拷贝
如果只是进行简单的赋值操作,是不会发生拷贝行为的。
>>> a = np.arange(12)
>>> b = a
>>> b is a # a和b仅仅是同一个对象的不同名字
True
>>> b.shape = 3,4
>>> a.shape
(3, 4)
这是由Python语言层面保证的,在进行赋值操作时,Python进行的时引用传递,两个变量指向同一块内存区域。
>>> def f(x):
... print(id(x))
...
>>> id(a)
148293216
>>> f(a)
148293216
视图或者浅拷贝
不同的ndarray对象可以共享相同的数据区。view
方法可以新建一个新的ndarray,这个新的ndarray和原始的ndarray不是一个对象(意味着除了数据区,其他一些属性,例如形状,都可以是不同的):
>>> c = a.view()
>>> c is a
False # c和a并不是一个对象
>>> c.base is a # c的数据来自于a
True
>>> c.flags.owndata
False
>>>
>>> c.shape = 2,6 # 修改c的形状属性并不会影响a
>>> a.shape
(3, 4)
>>> c[0,4] = 1234 # 由于二者共享数据区,修改c的数据也会影响到a
>>> a
array([[ 0, 1, 2, 3],
[1234, 5, 6, 7],
[ 8, 9, 10, 11]])
注意,索引操作返回的就是原始ndarray的一个view:
>>> s = a[ : , 1:3]
>>> s
array([[ 1, 2],
[ 5, 6],
[ 9, 10]])
>>> s[:] = 10
>>> s
array([[10, 10],
[10, 10],
[10, 10]])
>>>
>>> a # 修改s的数据区也会影响到a中的相应数据
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
深拷贝
copy
方法可以生成一个完整的新ndarray对象,这个ndarray对象和原始的ndarray没有任何关系:
>>> d = a.copy() # 新的对象,新的数据区
>>> d is a
False
>>> d.base is a
False
>>> d[0,0] = 9999
>>> a # 修改d的数据区并不会影响到a
array([[ 0, 10, 10, 3],
[1234, 10, 10, 7],
[ 8, 10, 10, 11]])
一个典型的应用场景是:如果使用索引操作后,原始的ndarray已经不需要了,那么就可以首先进行一个深拷贝,然后销毁原始ndarray,这样会减少内存消耗。
>>> a = np.arange(int(1e8))
>>> b = a[:100].copy()
>>> del a # a占据的内存会被释放