iT邦幫忙

2025 iThome 鐵人賽

DAY 13
0
自我挑戰組

數據新手村:統計系畢業生 30 天打怪升級之旅系列 第 13

Day 13 - 告別 for 迴圈:NumPy 的向量化運算魔法

  • 分享至 

  • xImage
  •  

大家好!歡迎來到數據新手村的第十三天。在前幾天的學習中,已經掌握了 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 內建的、更豐富的數學與統計函式庫,讓我們的分析能力更上一層樓。


上一篇
Day 12 - NumPy 陣列的索引與切片神技
下一篇
Day 14 - NumPy 實戰演練與總結:牛刀小試
系列文
數據新手村:統計系畢業生 30 天打怪升級之旅14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言