人工智能之數據分析 numpy

第十章 副本視圖


(文章目錄)


前言

在 NumPy 中,副本(copy)視圖(view) 是理解數組內存管理、性能優化和避免意外修改的關鍵概念。它們決定了當你對一個數組進行切片、變形或賦值操作時,​**是否創建了新的數據副本,還是僅僅創建了一個指向原數據的新“窗口”**​。

下面從原理、區別、判斷方法到實戰示例,詳細解析:


一、核心概念

概念 含義 是否共享數據 修改影響
副本(Copy) 完全獨立的新數組,擁有自己的內存空間 ❌ 不共享 修改副本不影響原數組
視圖(View) 原數組的一個“窗口”或“別名”,不復制數據 ✅ 共享 修改視圖會改變原數組

💡 ​關鍵點​:NumPy 默認儘可能返回 ​視圖​(為了節省內存和提升速度),只有在必要時才返回副本。


二、如何判斷是副本還是視圖?

使用數組的 .base 屬性:

  • 如果 arr.base is None這是一個副本(或原始數組)
  • 如果 arr.base is not None這是一個視圖,base 指向原始數組
import numpy as np

a = np.array([1, 2, 3, 4])
b = a.view()      # 顯式創建視圖
c = a.copy()      # 顯式創建副本
d = a[1:3]        # 切片(通常返回視圖)

print(b.base is a)  # True  → 視圖
print(c.base is a)  # False → 副本(c.base is None)
print(d.base is a)  # True  → 視圖(切片是視圖!)

三、常見操作:返回視圖 vs 副本

✅ 返回 視圖 的操作(共享數據)

操作 示例 説明
切片(slicing) a[1:4] 最常見!
np.view() a.view() 顯式創建視圖
reshape()(當可能時) a.reshape(2,2) 如果不改變數據佈局,返回視圖
轉置 T a.T 對高維數組通常是視圖
a = np.array([[1, 2], [3, 4]])
b = a.reshape(4,)   # 視圖(連續內存)
b[0] = 99
print(a)  # [[99, 2], [3, 4]] → 原數組被修改!

⚠️ 注意:reshape()不一定總是視圖​!如果無法在不復制數據的情況下 reshape(如非連續數組),NumPy 會自動返回副本。

✅ 返回 副本 的操作(獨立數據)

操作 示例 説明
np.copy() a.copy() 顯式深拷貝
花式索引(fancy indexing) a[[0, 2, 1]] 總是副本
布爾索引 a[a > 2] 總是副本
flatten() a.flatten() 總是返回副本
ravel()(有時) a.ravel() 儘量返回視圖,不行則副本(與 flatten 不同)
a = np.array([1, 2, 3, 4])

# 花式索引 → 副本
b = a[[0, 2]]
b[0] = 99
print(a)  # [1, 2, 3, 4] → 未變

# flatten → 副本
c = a.flatten()
c[0] = 88
print(a)  # 仍為 [1, 2, 3, 4]

四、reshape() 和 ravel() 的細節對比

方法 是否修改原數組 返回類型 內存行為
arr.reshape(...) 新數組 儘量視圖,否則副本
arr.resize(...) 無返回(in-place) 直接修改原數組形狀
arr.ravel() 一維數組 儘量視圖
arr.flatten() 一維數組 總是副本
a = np.array([[1, 2], [3, 4]])

# ravel() → 視圖(因為 a 是連續的)
b = a.ravel()
b[0] = 99
print(a)  # [[99, 2], [3, 4]]

# flatten() → 副本
c = a.flatten()
c[0] = 88
print(a)  # 仍是 [[99, 2], [3, 4]]

五、實戰陷阱:意外修改原數組

❌ 錯誤示例:以為切片是副本

data = np.array([10, 20, 30, 40])
subset = data[1:3]      # 這是視圖!
subset[0] = 999         # 你以為只改 subset?
print(data)             # [10, 999, 30, 40] → 原數組被改了!

✅ 正確做法:明確需要副本時用 .copy()

data = np.array([10, 20, 30, 40])
subset = data[1:3].copy()  # 顯式創建副本
subset[0] = 999
print(data)                # [10, 20, 30, 40] → 安全!

六、何時用視圖?何時用副本?

場景 推薦
只讀訪問子數據​(如分析某段信號) 用視圖(高效)
需要修改子數據但不想影響原數組 .copy()
函數內部處理數組,不確定是否會被修改 默認 .copy() 更安全
內存受限,且確定不會修改 用視圖節省內存

七、總結速查表

操作 返回 是否共享數據 安全修改?
a[:] 視圖 ❌(會影響 a)
a[1:3] 視圖
a[[1,2]] 副本
a[a>0] 副本
a.reshape(...) 視圖(儘量) 可能 謹慎
a.copy() 副本
a.view() 視圖
a.flatten() 副本
a.ravel() 視圖(儘量) 可能 謹慎

八、最佳實踐建議

  1. 永遠不要假設切片是副本 → 如需獨立數據,顯式調用 .copy()
  2. 在函數參數中接收數組時,若要修改,先 .copy() 避免副作用
  3. 使用 .base 屬性調試內存關係
  4. 處理大型數組時,優先使用視圖以節省內存,但要小心寫操作

後續

本文主要講述了numpy數組副本和視圖。python過渡項目部分代碼已經上傳至gitee,後續會逐步更新,主要受時間原因限制,當然自己也可以克隆到本地學習拓展。

資料關注

公眾號:咚咚王 gitee:https://gitee.com/wy18585051844/ai_learning

《Python編程:從入門到實踐》 《利用Python進行數據分析》 《算法導論中文第三版》 《概率論與數理統計(第四版) (盛驟) 》 《程序員的數學》 《線性代數應該這樣學第3版》 《微積分和數學分析引論》 《(西瓜書)周志華-機器學習》 《TensorFlow機器學習實戰指南》 《Sklearn與TensorFlow機器學習實用指南》 《模式識別(第四版)》 《深度學習 deep learning》伊恩·古德費洛著 花書 《Python深度學習第二版(中文版)【純文本】 (登封大數據 (Francois Choliet)) (Z-Library)》 《深入淺出神經網絡與深度學習+(邁克爾·尼爾森(Michael+Nielsen)》 《自然語言處理綜論 第2版》 《Natural-Language-Processing-with-PyTorch》 《計算機視覺-算法與應用(中文版)》 《Learning OpenCV 4》 《AIGC:智能創作時代》杜雨+&+張孜銘 《AIGC原理與實踐:零基礎學大語言模型、擴散模型和多模態模型》 《從零構建大語言模型(中文版)》 《實戰AI大模型》 《AI 3.0》