Numpy 副本與視圖
視圖是指對數據的引用,通過該引用亦便可訪問、操作原有數據,但原有數據不會產生拷貝。如果我們對視圖進行修改,它會影響到原始數據,物理內存在同一位置。
副本是一個數據的完整的拷貝,如果我們對副本進行修改,它不會影響到原始數據,物理內存不在同一位置。
視圖一般發生在:
- Numpy 的切片操作返回原數據的視圖;
- 調用 ndarray 的 view() 函數產生一個視圖。
副本一般發生在:
- 在對 Python 序列進行切片操作時,同時調用 deepcopy() 函數;
- 調用 ndarray (或其切片)的時候,同時調用 copy() 函數產生一個副本。
1. 直接賦值
1.1 ndarray 的賦值特性
對已經產生的 ndarray 對象,將該對象通過 = 方式再次賦值給其他變量,并不會創建數組對象的副本。即在該過程中產生的變量,都指向同一塊物理內存地址。
案例
對于不同的變量,可以用 id() 函數來查看其對應的通用標識符,進而判斷是否具有同一性。
a = np.arange(12)
print("數組a:", a)
print("數組a的id:", id(a))
打印結果為:
out:
數組a: [ 0 1 2 3 4 5 6 7 8 9 10 11]
數組a的id: 1383613133408
通過把 a 賦值給 b,創建一個新變量:
b = a
print("數組a:", b)
print("數組a的id:", id(b))
打印結果為:
數組a: [ 0 1 2 3 4 5 6 7 8 9 10 11]
數組a的id: 1383613133408
可以發現 a 和 b 的 id 完全一致,并且我們可以利用 is 判定符來佐證同一性的判定結論:
a is b
out:
True
案例
在對 a 進行修改操作時,響應的效果也會同步顯示在 b 變量中。
a.shape=(3,4)
a
out:
array([[ , 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
修改 a 為二維數組,相應的,b也會產生同樣的變化:
b
out:
array([[ 0, 1, 2, 3],
[ 4, 5, 6, 7],
[ 8, 9, 10, 11]])
2 視圖或淺拷貝
2.1 ndarray.view()
ndarray.view() 方會創建一個新的數組對象,該方法創建的新數組的維數更改不會更改原始數據的維數。
a = np.arange(6).reshape(3,2)
print("數組a:", a)
print("數組a的id:", id(a))
打印結果為:
數組a: [[0 1]
[2 3]
[4 5]]
數組a的id: 1383613212272
創建 a 的視圖 b:
b = a.view()
print("視圖b:", b)
print("視圖b的id:", id(b))
打印結果為:
視圖b: [[0 1]
[2 3]
[4 5]]
視圖b的id: 1383613212672
可以看到,在視圖產生的過程中,a 和 b 的 id 并不一致,這說明視圖和直接賦值是不一樣的。
案例
對視圖 b 進行元素修改,該修改會同步反饋在變量 a 中:
b[0,0]=100
print("數組b:", b)
print("數組a:", a)
打印結果為:
數組b: [[100 1]
[ 2 3]
[ 4 5]]
數組a: [[100 1]
[ 2 3]
[ 4 5]]
案例
對視圖 b 進行形狀修改,并不影響到 a:
b.shape=2,3
print("數組b:", b)
print("數組a:", a)
打印結果為:
數組b: array([[100, 1, 2],
[ 3, 4, 5]])
數組a: [[100 1]
[ 2 3]
[ 4 5]]
2.2 切片
使用切片創建視圖修改數組元素會影響到原始數組。
arr = np.arange(12)
print ("數組arr:", arr)
創建的 arr 數組為:
數組arr: [ 0 1 2 3 4 5 6 7 8 9 10 11]
分別通過切片產生 a 和 b:
a=arr[3:]
b=arr[3:]
print("修改前的切片a:", a)
print("修改前的切片b:", b)
切片結果 a 和 b 為:
修改前的切片a: [ 3 4 5 6 7 8 9 10 11]
修改前的切片b: [ 3 4 5 6 7 8 9 10 11]
分別改變切片 a 和 b 中的元素:
a[1]=123
b[2]=234
print("修改后的切片a:", a)
print("修改后的切片b:", b)
修改后的 a 和 b 為:
修改后的切片a: [ 3 123 234 6 7 8 9 10 11]
修改后的切片b: [ 3 123 234 6 7 8 9 10 11]
可以看到,對 a 和 b 所做的修改,都同時出現了。這說明切片直接是互相影響的。
print("修改后的原數組arr:", arr)
打印結果為:
修改后的原數組arr: [ 0 1 2 3 123 234 6 7 8 9 10 11]
綜合看下來,我們可以發現:變量 a,b 都是 arr 的一部分視圖,對視圖的修改會直接反映到原數據和相關切片中。
3 副本或深拷貝
3.1 ndarray.copy()
ndarray.copy() 函數創建一個副本。 對副本數據進行修改,不會影響到原始數據,它們物理內存不在同一位置。
案例
創建數組 a,并產生 a 的副本,記為 b:
a = np.array([[0,1], [2,3], [4,5]])
b = a.copy()
判斷 a 和 b 是否具有同一性:
b is a
out:
False
可以看到,a 和 b 互相獨立,這和賦值顯然不同。
對副本進行修改,觀察原始數組:
b[0,0]=100
print("修改后的數組b:", b)
print("原始數組a:", a)
打印結果為:
修改后的數組b: [[100 1]
[ 2 3]
[ 4 5]]
原始數組a: [[0 1]
[2 3]
[4 5]]
可以發現,副本產生的變化,并不會對原始數組產生影響。
4. 小結
本小節講解了視圖和副本的概念和區別。副本是對原始數組的完整拷貝,二者互相獨立,并不互相影響,但是物理內存的開銷會加倍。而視圖(切片)是對原始數據的一種映射,物理內存的開銷相對小一些;對視圖(切片)的元素更改,會相應地反映到原始數組中,這是二者最大的區別。