NumPy 1.14 教學 – #06 簡易指定(Simple Assignments), 檢視(Views), 深度拷貝(Deep Copy)
開始學NumPy之前至少先熟悉Python基礎使用方法,這樣再來看NumPy才不會那麼吃力!
Python3 教學、筆記NumPy提供了簡易指定Simple Assignments、檢視View、深度拷貝Deep Copy等方法,本文就會針對這幾種方法的差異做介紹!
這是因為陣列這類包含大量指標的變數,對於程式語言來說,通常會兩種複製的方法,一種是類似於捷徑的做法,另一種則是以建立完整的內容來達成相同的效果。但兩者在使用上會有不同的效果!在撰寫時也必須要視情況而定!本內容的的練習範例同步放置於GitHub:Learn NumPy – GitHub
簡易指定
簡易指定最主要的概念在於,NumPy會直接把變數的內容以記憶體指標指向你要的內容。
因此,這種做法是不會創造新的資料的。有點像是作業系統中軟捷徑的概念!
1 2 3 4 5 6 7 8 |
# 簡易指定並非真正複製內容 a = np.arange(12) # This will assign the a's address to b instead of copy the content of a to b. # 這會把a的記憶體位址指定給b, 並非真正複製內容 b = a print("b等於a嗎?:{0}".format(b is a)) print("a的形狀:{0}".format(a.shape)) print("b的形狀:{0}".format(b.shape)) |
1 2 3 |
b等於a嗎?:True a的形狀:(12,) b的形狀:(12,) |
我們來改變原陣列形狀,看看簡易指定後的變數是否也會跟著改變!!
1 2 3 4 5 6 |
# changes the shape of a # 改變a陣列的形狀 print("\n改變a的形狀") a.shape = 3, 4 print("a的形狀:{0}".format(a.shape)) print("b的形狀:{0}".format(b.shape)) |
1 2 3 |
改變a的形狀 a的形狀:(3, 4) b的形狀:(3, 4) |
簡易指定的變數,本質上根本就是指標。
後續會以『base、flags.owndata』這兩個屬性來比較view(淺拷貝, shadow copy)與深拷貝(deep copy)之間的差異。
1 2 3 4 5 |
print("The array of features of simple assignments:") print("簡單指定陣列的特性:") print("\tb is a:? {0}".format(b is a)) print("\tb is base on who:? {0}".format(b.base)) print("\tb.flags.owndata:? {0}".format(b.flags.owndata)) |
1 2 3 4 5 |
The array of features of simple assignments: 簡單指定陣列的特性: b is a:? True b is base on who:? None b.flags.owndata:? True |
檢視/淺拷貝
檢視就是淺拷貝,跟簡易指定很相似,但是相較起來檢視會比較正式喔!
首先,先把陣列宣告好~
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# View or Shallow Copy # 檢視/淺拷貝 # View # 檢視 a = np.arange(12) c = a.view() print("a=>\n{0}".format(a)) print("c=>\n{0}".format(c)) print() # Changes the shape of c. # 改變c陣列的形狀 c.shape = 3, 4 print("c.shape=3,4\nc=>\n{0}".format(c)) print("a=>\n{0}".format(a)) |
1 2 3 4 |
a=> [ 0 1 2 3 4 5 6 7 8 9 10 11] c=> [ 0 1 2 3 4 5 6 7 8 9 10 11] |
(1)變更c的形狀、(2)變更c[1, 2]的內容,來觀察a是否也會改變!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
print() # Changes the shape of c. # 改變c陣列的形狀 c.shape = 3, 4 print("c.shape=3,4\nc=>\n{0}".format(c)) print("a=>\n{0}".format(a)) print() # Let's change some element value of c. # 來變更c的元素值看看 c[1,2] = 999 print("c[1,2]=999\nc=>\n{0}".format(c)) print("a=>\n{0}".format(a)) print() # Slices an array will return a view of it. # If you change elements' value of the view, the related elements' value of source array will be changed simultaneously. # 對陣列/矩陣切片,會回傳檢視喔! # 如果你更改了檢視的元素值,來源陣列的相對應的元素值也會同時被改變! s = c[:, 1:3] print("s=c[:, 1:3]\ns=>\n{0}".format(s)) s[:] = 10 print("s[:] = 10\ns=>\n{0}".format(s)) print("c=>\n{0}".format(c)) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
c.shape=3,4 c=> [[ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11]] a=> [ 0 1 2 3 4 5 6 7 8 9 10 11] c[1,2]=999 c=> [[ 0 1 2 3] [ 4 5 999 7] [ 8 9 10 11]] a=> [ 0 1 2 3 4 5 999 7 8 9 10 11] s=c[:, 1:3] s=> [[ 1 2] [ 5 999] [ 9 10]] s[:] = 10 s=> [[10 10] [10 10] [10 10]] c=> [[ 0 10 10 3] [ 4 10 10 7] [ 8 10 10 11]] |
查看『base、flags.owndata』之後可以發現,檢視和簡易指定的差別在於owndata。
檢視的owndata值是False,代表檢視沒有自己的記憶體儲存空間,所以它是去參考別人的啦。(其實我也不太懂為何這個差異會造成使用上的差別) 原文解釋在這:numpy.ndarray.flags
此外,檢視(view)和簡易指定(simple assignments)使用上的差異在於,更改檢視的形狀是不會影響原本的參考來源變數的形狀喔!(可以看一下前面的例子)
1 2 3 4 5 6 |
print() print("The Features of View:") print("View的特性:") print("\tc is a:? {0}".format(c is a)) print("\tc is base on who:? {0}".format(c.base)) print("\tc.flags.owndata:? {0}".format(c.flags.owndata)) |
1 2 3 4 5 |
The Features of View: View的特性: c is a:? False c is base on who:? [ 0 10 10 3 4 10 10 7 8 10 10 11] c.flags.owndata:? False |
深度拷貝
深度拷貝和上述簡易指定與檢視最大不同之處在於,深度拷貝會建立一個完全獨立的物件,包括記憶體空間。
所以,變數b並沒有參考任何變數!b.base會顯示None。
numpy.ndarray.base:如果有參考其他物件的記憶體時,會顯示
1 2 3 4 5 6 7 8 9 10 11 12 |
##深度拷貝 a = np.arange(12) b = a.copy() print("a=>{0}".format(a)) print("b=>{0}".format(b)) print() print("The Features of Deep Copy:") print("Deep Copy的特性:") print("b is a:? {0}".format(b is a)) print("b is base on who:? {0}".format(b.base)) print("b.flags.owndata:? {0}".format(b.flags.owndata)) |
1 2 3 4 5 6 7 8 |
a=>[ 0 1 2 3 4 5 6 7 8 9 10 11] b=>[ 0 1 2 3 4 5 6 7 8 9 10 11] The Features of Deep Copy: Deep Copy的特性: b is a:? False b is base on who:? None b.flags.owndata:? True |
來測試一下!
1 2 3 4 5 6 7 8 9 |
print() b[5] = 99 print("b[5] = 999\nb=> {0}".format(b)) print("a=> {0}".format(a)) print() b.shape = 2, 6 print("b.shape = 2,6\nb=>\n{0}".format(b)) print("a=> {0}".format(a)) |
1 2 3 4 5 6 7 8 9 |
b[5] = 999 b=> [ 0 1 2 3 4 99 6 7 8 9 10 11] a=> [ 0 1 2 3 4 5 6 7 8 9 10 11] b.shape = 2,6 b=> [[ 0 1 2 3 4 99] [ 6 7 8 9 10 11]] a=> [ 0 1 2 3 4 5 6 7 8 9 10 11] |