National Central University
Uedu Main Site
Explore Uedu
Student Console
Register as Member/Login
Research Informed Consent Center
Survey Center
Educational Big Data Challenge
Claim or create a course
Try an AI Scenario Task
Support & Messages
Uptime Data

UeduGPTs

--

Jupyters

3

Local AI

--

Uedu Code

--

中央大學 AQI 80 24°C PM2.5 21
AI Reply Desktop Notifications

Show a desktop notification when the AI TA finishes replying

Chat Message Notifications

Notify me when classmates post messages in the forum

Sound notification

Play an alert sound whenever there is a new notification

Unit 02

NumPy 陣列

向量化運算、廣播、布林遮罩,為什麼不用迴圈
Interaction實作 30 minute(s)

Learning Objectives

  • 用 np.array 建立陣列,讀懂 shape、ndim 與 dtype
  • 用向量化運算取代 for 迴圈,並說出串列與陣列做 * 2 的差別
  • 在二維陣列上用 axis 算每一欄或每一列的統計量
  • 用廣播規則判斷兩個形狀能不能一起運算、結果是什麼形狀
  • 用布林遮罩篩選元素、計數與算比例,用 np.where 依條件換值

為什麼不用迴圈?

Python 的串列(list)什麼都能放,但要對每個元素做計算,就得寫迴圈:一個一個取出、計算、放回去。 資料有幾千、幾百萬筆時,這樣寫又長又慢。

NumPy 提供的 ndarray(n 維陣列)換了一種做法:陣列裡的元素都是同一種型別、在記憶體裡排在一起, 寫 arr * 60 時,NumPy 用編譯過的程式一次處理整個陣列。這種「對整個陣列下一個指令」的寫法叫向量化(vectorization):

  • 程式比較短,意思也比較清楚(「每個元素乘 60」就寫成 * 60)
  • 資料量大時比 Python 迴圈快很多
  • 下一課的 pandas 就建在 NumPy 上:資料表的每一欄底下都是一個陣列

這一課的範例都是自己打的幾個數字,用來看清楚每一步在做什麼。

逐步看:串列用迴圈,陣列一行完成

同一件事(把讀書時數換算成分鐘)寫兩次。注意右邊記憶體區:陣列 arr 畫成一列表格。

shape、ndim 與 dtype

每個陣列都有三個常看的屬性:

屬性 意思 np.array([[1.0, 2.0, 3.0], [4.0, 5.0, 6.0]])
shape 每一軸的長度 (2, 3):2 列 3 欄
ndim 軸的數目(維度) 2
dtype 元素的型別,整個陣列只有一種 float64(64 位元的小數)

dtype 是陣列和串列最大的不同:串列可以混放字串與數字,陣列裡全部是同一種型別。 建立陣列時,NumPy 依內容決定 dtype:有小數就是 float64,全是整數就是整數型別。

關於整數型別的一個提醒:np.array([1, 2, 3]).dtype 在一般的 64 位元電腦上通常是 int64; 但這個頁面的 Python 跑在瀏覽器裡(Pyodide,32 位元環境),會顯示 int32。 兩者能表示的範圍不同(int32 最大約 21 億),一般的成績、人數用不到這麼大;要處理很大的整數時,可以寫 np.array(..., dtype="int64") 指定型別。 pandas 讀 CSV 得到的整數欄,在兩個環境都是 int64。

二維陣列可以想成表格:scores[1] 取第 1 列(從 0 數起),scores[:, 0] 取第 0 欄,scores[1, 2] 取一格。 算統計量時用 axis 指定沿著哪一軸算:axis=0 是沿著列往下,每一欄得到一個數;axis=1 是沿著欄往右,每一列得到一個數。

逐步看:二維陣列與 axis

一個 4 位學生 × 3 次小考的成績表。每一步對照右邊的表格,看取出的是哪一列、哪一欄。

廣播:形狀不同也能一起算

scores + 5 把 5 加到每一格;scores + bonus(bonus 是三個數)把三個數分別加到三欄。 形狀不同的陣列能一起運算,靠的是廣播(broadcasting)規則:

  1. 兩個形狀從最右邊的軸開始對齊;維度比較少的,在左邊補長度 1。
  2. 每一軸:長度相同可以;其中一個是 1,就把它延伸成另一個的長度;其他情況出錯。
A 的形狀 B 的形狀 對齊 結果
(4, 3) 純量 純量延伸到每一格 (4, 3)
(4, 3) (3,) 3 與 3 相同;B 補成 (1, 3) 再延伸 (4, 3)
(4, 3) (4,) 最右軸 3 與 4 對不齊 錯誤
(4, 3) (4, 1) 3 與 1 → 延伸;4 與 4 相同 (4, 3)

第三列是最常見的錯:想讓每一列減去該列的平均(形狀 (4,)),卻因為對齊是從右邊開始而出錯。 解法是用 reshape(4, 1) 把它變成 4 列 1 欄。

逐步看:每格減去平均,以及一個對不齊的例子

同一個成績表,依序加純量、加一個 (3,) 的陣列、減去每次小考的平均,再試著減去每位學生的平均。

互動練習:選兩個形狀,看能不能廣播

選 A、B 的形狀,看逐軸對齊的結果。可以廣播時,虛線格是延伸出來的複製;點 C 的一格,看它來自哪兩格。下方是同一個運算的向量化寫法與等價的 for 迴圈。

布林遮罩:用條件挑元素

final >= 60 也是向量化運算:每個元素各比一次,得到一個同樣形狀、裝著 True/False 的布林陣列。常見的用法:

寫法 得到
final[final >= 60] 只留下條件為 True 的元素(布林遮罩)
(final >= 60).sum() True 當 1、False 當 0 加起來=符合條件的個數
(final >= 60).mean() 符合條件的比例
np.where(條件, 甲, 乙) 條件成立的位置放甲,否則放乙

兩個條件要同時成立用 &、任一成立用 |,每個條件都要加括號:final[(final >= 60) & (final < 80)]。 不能用 Python 的 and、or,因為它們只能判斷一個 True 或 False,不能逐個元素比較。

逐步看:及格人數、比例與加分

用固定種子產生 10 個期末分數(每次執行都一樣),再用布林陣列計數與篩選。

實作:每一科減去該科平均

起始碼有一個 3 位學生 × 4 科的成績表。請用向量化運算(不要寫迴圈)依序印出:

  1. 每一科(每一欄)的平均,四捨五入到小數 1 位
  2. 每一格減去該科平均的結果,四捨五入到小數 1 位
  3. 每一科有幾位學生高於該科平均
你的輸出
預期輸出

小測驗:陣列、axis、廣播與遮罩

1.

下面程式會印出什麼?

import numpy as np
a = np.array([1, 2, 3])
print(a * 2, [1, 2, 3] * 2)
2.

scores 的形狀是 (4, 3)。scores.mean(axis=0) 的形狀是什麼?

3.

下面哪一組和形狀 (4, 3) 的陣列一起運算時會出錯?

4.

下面程式會印出什麼?

import numpy as np
x = np.array([3, 8, 5, 10, 1])
print(x[x > 4].sum(), (x > 4).sum())
5.

對一百萬個數字做同樣的計算,NumPy 的向量化寫法通常比 Python 的 for 迴圈快,主要原因是什麼?

第 1 次作答

重點整理

  • NumPy 陣列的元素同一種型別;shape 是每一軸的長度、ndim 是軸數、dtype 是元素型別。整數陣列在這個頁面(Pyodide)是 int32,一般電腦通常是 int64。
  • 向量化:arr * 60、arr >= 60 一次對整個陣列運算,不寫迴圈;串列的 * 2 是重複,陣列的 * 2 是每個元素乘 2。
  • 二維陣列 scores[列, 欄];axis=0 沿著列往下(每一欄一個數),axis=1 沿著欄往右(每一列一個數)。
  • 廣播:形狀從右邊對齊,每一軸相同或其中一個是 1 才行;(4, 3) 與 (4,) 對不齊,要 reshape(4, 1)。
  • 布林遮罩:x[x > 4] 篩選、(x > 4).sum() 計數、(x > 4).mean() 比例、np.where 依條件換值;多個條件用 &、| 並加括號。
  • 下一課的 Series 是「加上索引的一維陣列」:每個值都有名字,運算時依名字對齊。
這一課完成了!
Next lesson:Series:有索引的一欄
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。