本文 AI 產出,尚未審核

Python – 資料處理(Data Processing)

主題:NumPy 陣列操作


簡介

在 Python 生態系統中,NumPy 是進行科學計算與資料處理的基石。它提供了高效能的多維陣列(ndarray)結構,讓我們能夠在數據清理、特徣抽取、機器學習前處理等工作中,將大量的迴圈運算交給底層的 C 程式碼完成,效能提升可達數十倍

本篇文章聚焦於 NumPy 陣列的基本操作與常見技巧,從建立陣列、切片、運算到進階的廣播(broadcast)與條件篩選,皆以 易懂的語言與實務範例 說明,適合剛入門的同學,也能為已有基礎的開發者提供快速參考。


核心概念

1. 建立與檢視 ndarray

import numpy as np

# 從 Python list 建立 1 維陣列
a = np.array([1, 2, 3, 4])
print("a:", a, " shape:", a.shape)          # (4,)

# 建立 2 維矩陣
b = np.array([[1, 2, 3],
              [4, 5, 6]])
print("\nb:\n", b)
print("b shape:", b.shape)                  # (2, 3)

# 使用內建函式快速產生特定形狀的陣列
zeros = np.zeros((3, 3))   # 3x3 全 0
ones  = np.ones((2, 4))    # 2x4 全 1
eye   = np.eye(4)          # 4x4 單位矩陣

重點np.array 會自動推斷資料型別,若想指定 dtype(例如 float64),可加上 dtype= 參數。


2. 陣列切片與索引

c = np.arange(0, 20, 2)   # [0, 2, 4, ..., 18]
print("\nc:", c)

# 1D 切片
print("c[2:5]:", c[2:5])          # [4 6 8]

# 2D 陣列的行列切片
print("\nb[0, :]:", b[0, :])      # 第 0 列全部
print("b[:, 1]:", b[:, 1])       # 第 1 欄全部

# 布林索引(Boolean indexing)
mask = b > 3                     # 產生布林矩陣
print("\nmask:\n", mask)
print("b[mask]:", b[mask])       # 只保留大於 3 的元素

技巧:使用 : 代表「全部」,可同時搭配 ...(省略號)快速存取高維陣列的子集。


3. 基本算術與廣播(Broadcast)

# 元素對元素的運算
x = np.array([1, 2, 3])
y = np.array([4, 5, 6])
print("\nx + y:", x + y)          # [5 7 9]

# 與標量的運算會自動廣播
print("x * 10:", x * 10)          # [10 20 30]

# 不同形狀的陣列相加(廣播規則)
A = np.arange(1, 7).reshape(2, 3)   # 2x3 矩陣
b = np.array([10, 20, 30])          # 1x3 向量
print("\nA + b:\n", A + b)          # 每列加上 b

概念:當兩個陣列的形狀不相同時,NumPy 會自動「延伸」較小的維度,使其與較大的維度相容,這就是 broadcast。若形狀不符合規則會拋出 ValueError


4. 常用的陣列操作函式

# 重新塑形(reshape)
d = np.arange(12)
print("\nd:", d)
e = d.reshape((3, 4))
print("e reshape to (3,4):\n", e)

# 軸向合併(concatenate, stack)
f = np.concatenate([e, e], axis=0)   # 直向堆疊
print("\nconcatenate along axis 0:\n", f)

g = np.stack([e, e], axis=2)         # 在第 2 軸新增維度
print("\nstack along new axis 2 shape:", g.shape)

# 統計函式
print("\nMean of e:", e.mean())
print("Sum of each column:", e.sum(axis=0))
print("Max value:", e.max())

備註reshape 只會改變視圖(view),不會複製資料;若需要複製,使用 copy()


5. 條件篩選與高階索引

# 取出符合條件的元素並保留原形狀(使用 where)
mask = (e % 2 == 0)               # 偶數位置
indices = np.where(mask)         # 回傳座標索引
print("\n偶數元素座標:", indices)

# 使用索引陣列直接取值
rows = np.array([0, 2])
cols = np.array([1, 3])
selected = e[rows, cols]         # 取 (0,1) 與 (2,3) 兩個元素
print("selected values:", selected)

實務技巧np.where 常用於「依條件產生新陣列」或「快速取代」:

result = np.where(e > 5, e, 0)   # 大於 5 的保留,其他設為 0

常見陷阱與最佳實踐

陷阱 說明 解決方式
資料型別不一致 混用 intfloat 會自動提升為較大類型,可能產生精度問題。 建立陣列時明確指定 dtype,如 np.array([...], dtype=np.float64)
切片返回視圖 slice 產生的是原陣列的 view,修改會影響原始資料。 若不想改動原陣列,使用 .copy() 產生獨立副本。
廣播失敗 形狀不符合 broadcast 規則會拋錯。 先檢查 arr.shape,必要時使用 np.expand_dimsreshape 調整。
大陣列記憶體爆炸 直接使用 np.concatenate 多次合併會產生大量暫存。 盡量先收集在 list 中,最後一次性 np.concatenate,或使用 np.vstack/np.hstack
使用 Python 迴圈 在大型資料上使用 for 迴圈會失去 NumPy 的向量化優勢。 儘可能改寫為 向量化運算,如 arr[arr > 0] = 0

最佳實踐

  1. 向量化:優先使用 NumPy 提供的算術與統計函式,避免手寫迴圈。
  2. 記憶體觀念:了解 view 與 copy 的差別,必要時使用 np.save / np.load 持久化大型陣列。
  3. 型別一致:在整個工作流程中保持相同的 dtype,尤其在與 pandas、scikit‑learn 互動時。
  4. 使用 np.einsum:對於複雜的張量運算,einsum 能在保持可讀性的同時提供極高效能。

實際應用場景

  1. 資料前處理:將 CSV 讀入後的 pandas.DataFrame 轉為 numpy.ndarray,利用切片快速抽取特徵欄位、執行缺失值填補 (np.nan_to_num)。
  2. 影像處理:圖像通常以 (height, width, channel) 的 3 維陣列儲存,使用切片即可取得單一通道,或利用 broadcast 調整亮度 (img * 1.2)。
  3. 科學模擬:在物理或金融模型中,大量矩陣運算(如矩陣乘法 np.dot、特徵值分解 np.linalg.eig)都是基於 NumPy 陣列完成的。
  4. 機器學習特徵工程:利用條件篩選 (np.where)、標準化 ((x - x.mean()) / x.std()) 等向量化步驟,讓資料在送入模型前已完成大部分清理。

總結

NumPy 的 ndarray 不僅是 Python 資料科學的基礎,更是日常資料處理、模型前處理與高效運算的利器。透過本篇的 建立、切片、算術、廣播、統計與條件篩選 等核心概念,我們已掌握了最常用的陣列操作技巧。
在實務開發中,避免迴圈、注意資料型別、善用 view 與 copy,即可寫出既簡潔又高效的程式碼。希望讀者能把這些概念立即應用到自己的專案中,體驗 NumPy 帶來的速度與便利。祝學習愉快,程式碼寫得更順手!