numpy關於視圖和副本的區別

 

 

numpy在數據處理時的優勢時顯而易見的,談到numpy,會經常聽到關於視圖和副本的說法,今天就來記錄一下視圖個副本的區別。

首先講一下兩者的定義:

副本是一個數據的完整的拷貝,如果我們對副本進行修改,它不會影響到原始數據,物理內存不在同一位置。

視圖是數據的一個別稱或引用,通過該別稱或引用亦便可訪問、操作原有數據,但原有數據不會產生拷貝。如果我們對視圖進行修改,它會影響到原始數據,物理內存在同一位置。

理解這個定義其實可以參考python的深淺拷貝的概念,副本其實就是類似於深拷貝,是完完整整的脫離了原來的數據,內存地址獨立出來,可以進行任意的修改和操作,對原始的數據不會產生影響,視圖類似於淺拷貝(可能這個對比並不完美),通俗一點說就是多了一個指向原來數據地址的路徑,還是要依附於原來的數據,任意的操作都有可能對原始數據產生影響,所以,再進行數據處理的時候,最好是先做副本的處理,這樣 數據處理過程中有什麼差錯都可以保留原始數據的完整性。

視圖一般發生在:

  • 1、numpy 的切片操作返回原數據的視圖。
  • 2、調用 ndarray 的 view() 函數產生一個視圖。

副本一般發生在:

  • Python 序列的切片操作,調用deepCopy()函數。
  • 調用 ndarray 的 copy() 函數產生一個副本。

無複製

簡單的賦值不會創建數組對象的副本。 相反,它使用原始數組的相同id()來訪問它。 id()返回 Python 對象的通用標識符,類似於 C 中的指針。

 

此外,一個數組的任何變化都反映在另一個數組上。 例如,一個數組的形狀改變也會改變另一個數組的形狀。

視圖或淺拷貝

ndarray.view() 方會創建一個新的數組對象,該方法創建的新數組的維數更改不會更改原始數據的維數

import numpy as np 
 
# 最開始 a 是個 3X2 的數組
a = np.arange(6).reshape(3,2)  
print ('數組 a:')
print (a)
print ('創建 a 的視圖:')
b = a.view()  
print (b)
print ('兩個數組的 id() 不同:')
print ('a 的 id():')
print (id(a))
print ('b 的 id():' )
print (id(b))
# 修改 b 的形狀,並不會修改 a
b.shape =  2,3
print ('b 的形狀:')
print (b)
print ('a 的形狀:')
print (a)

運行結果是:

數組 a:
[[0 1]
 [2 3]
 [4 5]]
創建 a 的視圖:
[[0 1]
 [2 3]
 [4 5]]
兩個數組的 id() 不同:
a 的 id():
4314786992
b 的 id():
4315171296
b 的形狀:
[[0 1 2]
 [3 4 5]]
a 的形狀:
[[0 1]
 [2 3]
 [4 5]]

使用切片創建視圖修改數據會影響到原始數組:

import numpy as np 
 
arr = np.arange(12)
print ('我們的數組:')
print (arr)
print ('創建切片:')
a=arr[3:]
b=arr[3:]
a[1]=123
b[2]=234
print(arr)
print(id(a),id(b),id(arr[3:]))

運行結果如下:

我們的數組:
[ 0  1  2  3  4  5  6  7  8  9 10 11]
創建切片:
[  0   1   2   3 123 234   6   7   8   9  10  11]
4545878416 4545878496 4545878576

變量 a,b 都是 arr 的一部分視圖,對視圖的修改會直接反映到原數據中。但是我們觀察 a,b 的 id,他們是不同的,也就是說,視圖雖然指向原數據,但是他們和賦值引用還是有區別的。

副本或深拷貝

ndarray.copy() 函數創建一個副本。 對副本數據進行修改,不會影響到原始數據,它們物理內存不在同一位置。 

import numpy as np 
 
a = np.array([[10,10],  [2,3],  [4,5]])  
print ('數組 a:')
print (a)
print ('創建 a 的深層副本:')
b = a.copy()  
print ('數組 b:')
print (b)
# b 與 a 不共享任何內容  
print ('我們能夠寫入 b 來寫入 a 嗎?')
print (b is a)
print ('修改 b 的內容:')
b[0,0]  =  100  
print ('修改後的數組 b:')
print (b)
print ('a 保持不變:')
print (a)

運行結果如下:

數組 a:
[[10 10]
 [ 2  3]
 [ 4  5]]
創建 a 的深層副本:
數組 b:
[[10 10]
 [ 2  3]
 [ 4  5]]
我們能夠寫入 b 來寫入 a 嗎?
False
修改 b 的內容:
修改後的數組 b:
[[100  10]
 [  2   3]
 [  4   5]]
a 保持不變:
[[10 10]
 [ 2  3]
 [ 4  5]]

ps:這裏再強調一下賦值和淺拷貝的區別,賦值就是引用,賦值的兩端的id是一樣的,改變兩個中的任意一個都會使另一個發生變化,淺拷貝的話,id是不一樣的,只拷貝了第一層,深層的並沒有拷貝。

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章