大家好!歡迎來到數據新手村的第十三天。在前幾天的學習中,已經掌握了 NumPy 陣列的創建和索引。今天要來學習 NumPy 之所以成為數據科學基石的真正原因——向量化運算 (Vectorization)。
想像一下,如果要把一個包含一百萬個數字的 Python 列表,每個數字都加上 5,直覺的寫法會是這樣:
# 傳統 for 迴圈作法
my_list = list(range(1_000_000))
new_list = []
for item in my_list:
new_list.append(item + 5)
這段程式碼很直觀,但非常慢。因為 Python 直譯器需要執行一百萬次迴圈。
而 NumPy 的「向量化」思想是:將運算直接作用於整個陣列,而不是單一元素。
import numpy as np
# NumPy 向量化作法
my_array = np.arange(1_000_000)
new_array = my_array + 5
這段程式碼不僅簡潔,而且快上數十甚至數百倍!因為 + 5 這個運算,被 NumPy 直接下放到預先編譯好的 C 語言底層去執行,擺脫了 Python for 迴圈的效能瓶頸。
比喻: for 迴圈就像是一位指揮官,對一百萬個士兵單獨下達指令;而向量化則是直接對整個軍隊下達一個「全體前進五步!」的命令,效率天差地遠。
向量化算術運算
NumPy 中所有的標準算術運算,都支援向量化。
陣列與純量 (Scalar) 的運算
# 一維np算術運算
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a ** 2)
結果輸出:
[5 7 9]
[-3 -3 -3]
[ 4 10 18]
[0.25 0.4 0.5 ]
[1 4 9]
# 原始運算
c = [1, 2, 3]
d = [4, 5, 6]
for i in range(len(c)):
d[i] = c[i] + d[i]
print(d[i])
結果輸出:
5
7
9
陣列與陣列之間的運算
當兩個陣列的形狀 (shape) 完全相同時,它們可以進行「逐元素 (element-wise)」的運算。
# 二維np算術運算
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([[4, 5, 6], [7, 8, 9], [1, 2, 3]])
print(a + b)
print(a - b)
print(a * b)
print(a / b)
print(a ** 2)
結果輸出:
[[ 4 10 18]
[28 40 54]
[ 7 16 27]]
[[0.25 0.4 0.5 ]
[0.57142857 0.625 0.66666667]
[7. 4. 3. ]]
[[ 1 4 9]
[16 25 36]
[49 64 81]]
# 陣列與標量之間的算術運算
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
print(a + 3)
print(a * 3)
結果輸出:
[[ 4 5 6]
[ 7 8 9]
[10 11 12]]
[[ 3 6 9]
[12 15 18]
[21 24 27]]
神奇的廣播機制 (Broadcasting)
那如果兩個陣列的形狀不完全相同呢?這時,NumPy 強大的「廣播機制」就會登場。只要符合特定規則,NumPy 會自動「擴展」(廣播) 較小的陣列,以匹配較大陣列的形狀,然後再進行向量化運算。
# 廣播機制:1.獲取形狀 2.是否可以廣播 3.進行廣播
# 條件: 所有對應維度的大小必須相等,或是其中一個為 1。
a = np.array([1, 2, 3]) #1*3
b = np.array([[4], [5], [6]]) # 3*1
print(a + b)
print(b - a)
結果輸出:
[[5 6 7]
[6 7 8]
[7 8 9]]
[[3 2 1]
[4 3 2]
[5 4 3]]
廣播是一個進階但非常有用的特性,它讓許多複雜的運算變得異常簡潔。
# 錯誤範例:不同維度
# a = np.array([1, 2, 3]) #1*3
# b = np.array([4, 5]) # 1*2
# print(a + b)
# ValueError: operands could not be broadcast together with shapes (3,) (2,)
# 矩陣運算
a = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
b = np.array([[4, 5, 6], [7, 8, 9], [1, 2, 3]])
# 「元素對應相乘」(Element-wise Multiplication)
print(a * b)
# 「矩陣乘法」(Matrix Multiplication)
# 求[n,m]的值,等於矩陣1的第n行的值依序乘以矩陣2的第m列的值,然後把相乘的結果求和
print(a @ b)
結果輸出:
[[ 4 10 18]
[28 40 54]
[ 7 16 27]]
[[ 21 27 33]
[ 57 72 87]
[ 93 117 141]]
結語
今天見識了 NumPy 的效能核心——向量化。請記住這個原則:在 NumPy 中,盡一切可能去避免手寫 for 迴圈,優先使用向量化的陣列運算。 這是寫出高效能、也更簡潔的數據分析程式碼的關鍵。
學會了基本的加減乘除,明天 Day 14,我們將探索 NumPy 內建的、更豐富的數學與統計函式庫,讓我們的分析能力更上一層樓。