NumPy - 複製與檢視



在 NumPy 中,當您對陣列執行操作時,結果可能是原始資料的副本或只是原始資料的檢視。瞭解這兩者之間的區別對於有效的記憶體管理以及避免程式碼中的意外副作用非常重要。

在 NumPy 中建立副本

我們可以使用 copy() 函式在 NumPy 中顯式地建立陣列的副本。此函式生成一個新陣列,並將原始陣列中的資料複製到此新陣列中。

當您在 NumPy 中建立陣列的副本時,資料將被完全複製。這意味著對副本進行的更改不會影響原始陣列,反之亦然。當您需要使用修改後的陣列版本而不更改原始資料時,副本很有用。

示例

在以下示例中,修改copied_array不會影響original_array,這證明了這兩個陣列的獨立性:

import numpy as np

# Original array
original_array = np.array([1, 2, 3])

# Creating a copy
copied_array = original_array.copy()

# Modifying the copy
copied_array[0] = 10

print("Original Array:", original_array)  
print("Copied Array:", copied_array)     

獲得以下輸出:

Original Array: [1 2 3]
Copied Array: [10  2  3]

淺複製與深複製

在 NumPy 陣列的上下文中,淺複製和深複製之間的區別對於理解複製資料的方式很重要。

淺複製

陣列的淺複製建立一個新的陣列物件,但如果這些元素本身是陣列或其他複雜物件,則它不會建立原始陣列中包含的元素的副本。

相反,新陣列仍然引用與原始陣列相同的元素。這意味著對元素內容的更改將影響原始陣列和複製陣列。

  • 陣列級副本 - 在 NumPy 陣列的情況下,淺複製意味著雖然複製了頂層陣列物件,但底層資料緩衝區沒有被複制。新陣列只是同一資料的新的檢視。
  • 用法 - 當您需要一個新的陣列物件但又想避免複製大量資料的開銷時,淺複製很有用。

示例

在此示例中,修改shallow_copy也會修改original_array,因為它們共享相同的基礎資料:

import numpy as np

# Original array
original_array = np.array([[1, 2, 3], [4, 5, 6]])

# Shallow copy
shallow_copy = original_array.view()

# Modify an element in the shallow copy
shallow_copy[0, 0] = 100

print("Original Array:")
print(original_array)

print("\nShallow Copy:")
print(shallow_copy)    

這將產生以下結果:

Original Array:
[[100   2   3]
 [  4   5   6]]

Shallow Copy:
[[100   2   3]
 [  4   5   6]]

深複製

另一方面,深複製會建立一個新的陣列物件以及它包含的所有資料的副本。這意味著對新陣列進行的任何更改都不會影響原始陣列,反之亦然。新陣列中的資料與原始陣列中的資料完全獨立。

  • 完全複製 - 在 NumPy 的上下文中,深複製涉及複製陣列的整個資料緩衝區,以確保新陣列與原始陣列完全分離。
  • 用法 - 當您需要獨立於原始陣列使用資料時,深複製非常重要,尤其是在資料可能以不應影響原始資料的方式修改時。

示例

在這種情況下,修改deep_copy不會影響original_array,這證明了這兩個陣列的獨立性:

import numpy as np

# Original array
original_array = np.array([[1, 2, 3], [4, 5, 6]])

# Deep copy
deep_copy = original_array.copy()

# Modify an element in the deep copy
deep_copy[0, 0] = 100

print("Original Array:")
print(original_array)

print("\nDeep Copy:")
print(deep_copy)   

以下是上述程式碼的輸出:

Original Array:
[[1 2 3]
[4 5 6]]

Deep Copy:
[[100   2   3]
 [  4   5   6]]

複製子陣列

為了避免在處理子陣列時修改原始陣列,您應該建立子陣列的副本。當您需要獨立於原始資料操作或分析子陣列時,這很有用。

子陣列只是現有 NumPy 陣列的一部分。您可以使用切片技術提取子陣列。

例如,如果您有一個二維陣列,您可以透過沿著其行和列進行切片來提取一個較小的二維子陣列。但是,預設情況下,切片會建立原始陣列的檢視,而不是單獨的副本。這意味著對子陣列的更改也將影響原始陣列,除非您顯式地建立副本。

示例

在下面的示例中,sub_array由於使用了 copy() 函式,因此是一個完全獨立的陣列:

import numpy as np

# Original 2D array
original_array = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])

# Creating a copy of the subarray
sub_array = original_array[0:2].copy()
sub_array[0] = 20

print("Original Array after subarray copy:", original_array)  
print("Subarray:", sub_array)                                 

獲得的輸出如下所示:

Original Array after subarray copy: 
[[1 2 3]
 [4 5 6]
 [7 8 9]]
Subarray: 
[[20 20 20]
 [ 4  5  6]]

在 NumPy 中建立檢視

當您切片陣列或執行某些操作(如重塑)時會建立檢視。資料不會被複制;相反,新陣列只是檢視原始資料的一種不同方式。

換句話說,檢視是一個新的陣列物件,它檢視與原始陣列相同的資料。這意味著,如果您修改檢視,更改將反映在原始陣列中,反之亦然。

示例

在此示例中,修改view_array直接影響original_array,表明它們共享相同的資料:

import numpy as np

# Original array
original_array = np.array([1, 2, 3])

view_array = original_array[0:2]

# Modifying the view
view_array[0] = 30

print("Original Array after view modification:", original_array) 
print("View Array:", view_array)                                   

執行上述程式碼後,我們將獲得以下輸出:

Original Array after view modification: [30  2  3]
View Array: [30  2]

何時返回檢視?

並非所有切片或操作都會導致檢視。如果陣列的記憶體佈局發生變化,NumPy 可能會返回副本而不是檢視。

來自切片的檢視

NumPy 中返回檢視的最常見情況是當您切片陣列時。切片是透過指定一系列索引來提取陣列一部分的方法。NumPy 返回一個檢視,而不是建立一個具有自身資料的新陣列,這意味著切片陣列與原始陣列共享相同的資料。

示例

在此示例中,view_arrayoriginal_array的檢視。資料不會被複制,並且兩個陣列共享相同的底層記憶體。這意味著對“view_array”進行的任何更改也將影響“original_array”:

import numpy as np

# Original array
original_array = np.array([1, 2, 3, 4, 5])

# Creating a view by slicing the original array
view_array = original_array[1:4]

print("Original Array:")
print(original_array)

print("\nView Array (Sliced):")
print(view_array)

產生的結果如下:

Original Array:
[1 2 3 4 5]

View Array (Sliced):
[2 3 4]

來自重塑的檢視

返回檢視的另一種常見情況是當您重塑陣列時。重塑會更改陣列的形狀(即每個維度中的元素數),而不更改底層資料。在可能的情況下,NumPy 會以新形狀返回原始陣列的檢視。

示例

這裡,reshaped_arrayoriginal_array的檢視,只是以“2x3”格式呈現。資料保持不變,修改“reshaped_array”也會修改“original_array”:

import numpy as np

# Original 1D array
original_array = np.array([1, 2, 3, 4, 5, 6])

# Reshaping the array into a 2x3 matrix
reshaped_array = original_array.reshape(2, 3)

print("Original Array:")
print(original_array)

print("\nReshaped Array (View):")
print(reshaped_array)

我們獲得以下輸出:

Original Array:[1 2 3 4 5 6]
Reshaped Array (View):
[[1 2 3]
 [4 5 6]]

來自轉置的檢視

轉置陣列涉及將其翻轉過其對角線,將行轉換為列,反之亦然。當您使用np.transpose()函式或.T屬性轉置陣列時,NumPy 會在可能的情況下返回檢視,而不是副本。

示例

在這種情況下,transposed_arrayoriginal_array的檢視,但軸已交換。底層資料保持不變,對“transposed_array”的更改將反映在“original_array”中:

import numpy as np

# Original 2D array
original_array = np.array([[1, 2, 3], [4, 5, 6]])

# Transposing the array
transposed_array = original_array.T

print("Original Array:")
print(original_array)

print("\nTransposed Array (View):")
print(transposed_array)

獲得以下輸出:

Original Array:
[[1 2 3]
 [4 5 6]]

Transposed Array (View):
[[1 4]
 [2 5]
 [3 6]]

base 屬性

在 NumPy 中,陣列的base屬性檢查陣列是另一個數組的檢視還是副本。它是對從中派生當前陣列的原始陣列的引用。

如果當前陣列是另一個數組的檢視,“base”將指向該原始陣列。如果當前陣列不是檢視(即它是原始陣列或深複製),則“base”將為None

示例:原始陣列的 base 屬性

當您建立陣列時,其 base 屬性將為 None,因為它就是原始陣列:

import numpy as np

# Creating an original array
original_array = np.array([10, 20, 30, 40, 50])

# Checking the base attribute
print("Base of original array:", original_array.base)

這將產生以下結果:

Base of original array: None

示例:檢視的 base 屬性

當您建立陣列的檢視(例如,透過切片)時,檢視的 base 屬性將指向原始陣列:

import numpy as np

# Creating an original array
original_array = np.array([10, 20, 30, 40, 50])

# Creating a view of the original array
view_array = original_array[1:4]

# Checking the base attribute
print("Base of view array:", view_array.base)

以下是上述程式碼的輸出:

Base of view array: [10 20 30 40 50]

示例:副本的 base 屬性

如果您建立陣列的副本,則 base 屬性將為 None,表示複製的陣列獨立於原始陣列:

import numpy as np

# Creating an original array
original_array = np.array([10, 20, 30, 40, 50])

# Creating a copy of the original array
copy_array = original_array.copy()

# Checking the base attribute
print("Base of copy array:", copy_array.base)

以下是上述程式碼的輸出:

Base of copy array: None
廣告