🗺️ AI 學習與考證地圖
中級科目二程式實戰 · 資料篩選與合併

兩道關卡的訂單過濾:
9 筆進去、4 筆出來,還帶上 region

先用 query 建一張活躍客戶名單,再讓訂單闖兩道關卡:關卡一金額 >= 1000(實跑 9 筆剩 7 筆——999 差一元出局、1000 整整好過關);關卡二 how='inner' 對名單(7 筆剩 4 筆——5000 元大單也死,因為客戶 inactive;還有一筆 3000 元的孤兒單,客戶表裡根本查無此人)。活著出來的 4 筆,每筆多了一欄 region——merge 從客戶表帶進來的通行章。

閱讀模式

00題目

閱讀下列程式,result 最終保留哪些資料?

active = customers.query("status == 'active'")   # 名單:只留活躍客戶
result = (orders[orders['amount'] >= 1000]          # 關卡一:金額 >= 1000 的訂單
          .merge(active[['customer_id', 'region']],   # 只帶 key 與 region 兩欄來併
                 on='customer_id', how='inner'))      # 關卡二:兩邊都有的 customer_id 才留

先說:夜店門口的兩個保鑣

想像 result 是夜店裡的人。門口站兩個保鑣:保鑣一看消費力——低消 1000,帶 999 的擋在門外(差一元也不行,但剛好 1000 放行——「至少」含等號);保鑣二對會員名單——名單是先用 query 印好的「活躍會員」清冊,不在名單上就出局,你消費力再高也一樣(實跑:5000 元的 O3 就死在這裡)。

兩關都過的人,手背蓋一個章——region:這個章原本不在訂單上,是保鑣二從會員清冊上抄過來的。這就是 merge 的雙重身分:既是過濾器(inner 刷人),又是搬運工(帶欄位進來)

三個先立好的事實: query("status == 'active'")customers[customers['status']=='active']同一件事的兩種寫法(實跑逐列相同)——題目故意一行用 query、一行用布林遮罩。 how='inner'交集:customer_id 兩邊都有的列才留。 active[['customer_id','region']] 只挑兩欄來併——key 用來對名單、region 用來帶進 result,其他欄(status)不跟來。

先點開看:query 字串篩選inner 交集兩道關卡

不熟資料處理名詞?你需要先認識下列名詞

點擊後出現漸進式說明:白話說明 → 說清楚一點 → 常見錯誤與考點。

兩種篩選法
query 字串篩選布林遮罩>= 至少999 與 1000 的邊界
merge 的零件
merge 合併on:對哪個 keyhow:用哪種方式[['a','b']] 選欄再併
四種 how
inner 交集left 保左邊outer 聯集對不到 → NaN
考場的暗器
孤兒訂單anti-join(D 在講的)一對多展開SQL 對照

不熟 Python?你需要先認識下列名詞

點擊後出現漸進式說明

表格的世界
DataFrame 表格"status == 'active'" 字串條件
括號三兄弟
[[ ]] 與 [ ] 的差別( ) 換行接龍== 比較
名字與慣例
= 指派變數 active/result

01逐行拆解:名單一行、關卡三行

四行,一行一行走完。右上角的「看位置」可以把這一行放回完整程式裡看。

第 1 行query:先印好會員名單
active = customers.query("status == 'active'")   # 6 位客戶裡挑出活躍的——名單存進 active

query一串文字描述篩選條件——引號裡的 status == 'active' 讀作「status 欄等於 'active' 的列」。實跑:6 位客戶剩 4 位(C1、C3、C4、C6),C2、C5 因 inactive 落選。它與 customers[customers['status']=='active']同一件事的兩種寫法(實跑逐列相同)——題目故意兩種都用,考你認不認得出來。

相關名詞:query 字串篩選字串條件active 子集

第 2 行關卡一:金額至少 1000
result = (orders[orders['amount'] >= 1000]   # 布林遮罩:金額 >= 1000 的訂單才往下走

這行用的是布林遮罩寫法:orders['amount'] >= 1000 對每筆訂單打 True/False,外層的 orders[...] 只留 True 的列。實跑:9 筆剩 7 筆——O2(800)與 O5(999)出局。注意邊界:>= 含等號,O4 的 1000 整整好過關;選項 A 說「至少 1000」,就是在講這個等號。開頭的 ( 是為了讓整條鏈可以優雅換行——跟結尾的 ) 成對。

相關名詞:布林遮罩>= 至少999 與 1000 的邊界( ) 換行接龍

第 3 行merge 的原料:只帶兩欄來併
          .merge(active[['customer_id', 'region']],   # 從名單只挑 key 與 region 兩欄

active[['customer_id', 'region']]雙中括號從名單挑兩欄:customer_id 是待會對名單用的 keyregion 是要帶進 result 的贈品——status 等其他欄不跟來,結果表才乾淨。這是實務的好習慣:merge 前先把右表瘦身成「key + 要帶的欄」,省記憶體也防止欄位大爆炸。

相關名詞:[['a','b']] 選欄再併[[ ]] 與 [ ]region 是帶進來的

第 4 行關卡二:on 對 key、how='inner' 取交集
                 on='customer_id', how='inner'))   # 兩邊都有這個 customer_id 的列才留

on='customer_id' 指定用哪個欄對名單how='inner' 指定對不到的怎麼辦:刷掉——只留兩邊都有的 customer_id(交集)。實跑:過了關卡一的 7 筆,再被刷掉 3 筆——O3(5000 元,但 C2 是 inactive)、O7(1500 元,C5 inactive)、O8(3000 元,C7 根本不在客戶表)——金額再大,名單對不到就出局。最終 4 筆存活:O1、O4、O6、O9,每筆帶上 region

一句話記住本題:篩金額、對名單、inner 取交集——兩關都過才留下,region 是通行證上的章。選項 A 的每個字:「至少 1000」=關卡一(含等號)、「對到 active 客戶」=關卡二、「帶入 region」= merge 的搬運工身分。

相關名詞:on:對哪個 keyhow:用哪種方式inner 交集

02名單怎麼印:query 與布林遮罩是同一件事

本站實跑的客戶表 6 位。第 1 行的 query 印出活躍名單——跟第 2 行用的布林遮罩其實是同一招的兩種寫法

互動實驗室:兩種篩選法對照query 字串 vs 布林遮罩——實跑逐列相同
customer_idstatusregion下場
C1active北區入選名單
C2inactive中區落選
C3active南區入選名單
C4active北區入選名單
C5inactive南區落選
C6active東區入選(但它沒有訂單——記住它)
本站實跑:兩種寫法的結果用 .equals() 比對——逐列相同(True)。active 名單= C1、C3、C4、C6 共 4 位。題目一行用 query、一行用遮罩不是手滑,是考你認得出「它們是同一件事」——考場上兩種寫法都要能秒讀。

相關名詞:query 字串篩選布林遮罩status 欄與名單

03九筆訂單的命運:9 → 7 → 4

每一筆訂單都要闖兩道關卡。點任一張卡,看它的完整死因或生還理由:

互動實驗室:九筆訂單追蹤器綠=存活、黃=死在關卡一、紅=死在關卡二
進場訂單
9
orders 全部
過關卡一(amount ≥ 1000)
7
O2、O5 出局
過關卡二(inner 對名單)
4
O3、O7、O8 出局
本站實跑 result(4 筆 × 4 欄):O1(C1,1200,北區)、O4(C3,1000,南區)、O6(C4,2500,北區)、O9(C1,30000,北區)——欄位 order_id / customer_id / amount / regionregion 是 merge 從客戶名單帶進來的。兩個亮點:O4 的 1000 元整整好過關(>= 含等號);O3 的 5000 元死在第二關——金額是全場第二高,但 C2 不在活躍名單上。

相關名詞:兩道關卡999 與 1000 的邊界孤兒訂單

04把 how 轉一圈:inner、left、outer 各留下誰

選項 B 說「所有訂單都保留;對不到客戶者的 region 填入 active」。把 how 真的轉一圈(全部實跑),看它錯在哪:

互動實驗室:merge 方式切換器同一次合併,三種 how 三張表
B 的兩層死因(實跑): 題目用的是 inner——對不到名單的列整列刷掉,不是保留;就算好心幫 B 改成 how='left',也只是「過了關卡一的 7 筆」全留(關卡一刷掉的 O2、O5 照樣不在),離「所有訂單都保留」還差兩筆。 left 之下對不到的 region 是 NaN(缺值)——實跑 O3、O7、O8 三筆的 region 全是 NaN——pandas 不會把缺格「填入 active」這種字串;'active' 是 status 欄的值,跟 region 欄八竿子打不著。

相關名詞:left 保左邊outer 聯集對不到 → NaN

05選項法庭:C 的空集合、D 的反向合併

剩下兩個選項描述的集合,我們也真的算出來了。開庭,兩件展品:

互動實驗室:選項法庭C 與 D 描述的集合——實跑現形
判決:C 描述的「amount 小於 1000 的 inactive 客戶訂單」——實跑是空集合(0 筆):兩筆小額單 O2、O5 的主人 C1、C3 都是 active——C 不只方向全反,連個例子都湊不出來。D 描述的「沒有任何訂單的 active 客戶」——那是反向合併(anti-join)的工作,實跑只有 C6 一位;而 inner 的 result 是「訂單 × 客戶」的交集,沒有訂單的客戶根本不可能出現在裡面(實跑 result 查無 C6)。

相關名詞:anti-join(D 在講的)active 子集inner 交集

06考場加碼:SQL 對照、四種 how、一對多陷阱

這題就是 SQL 的老朋友換 pandas 皮。對照表一次備齊:

-- 同一件事的 SQL 寫法
SELECT o.order_id, o.customer_id, o.amount, c.region   # 訂單欄位+帶入 region
FROM orders o                                          # 左表:訂單
INNER JOIN customers c ON o.customer_id = c.customer_id   # 關卡二:對名單取交集
WHERE o.amount >= 1000 AND c.status = 'active'      # 關卡一+名單條件
how語意本題實跑筆數對不到的下場
inner(題目)交集:兩邊都有 key 才留4整列刷掉
left左表全留7右欄補 NaN(O3/O7/O8)
right右表全留左欄補 NaN(C6 會以空訂單現身)
outer聯集:兩邊都全留8缺的那側補 NaN

一對多陷阱(實跑):merge 不是 VLOOKUP——key 在右表出現幾次,左表那列就複製幾份。我們故意把名單裡的 C1 重複一次再併:result 從 4 筆膨脹成 6 筆(O1、O9 各複製兩份)。實務上 merge 前先確認右表 key 唯一(active['customer_id'].is_unique),報表金額才不會默默翻倍——「不報錯的錯」家族又一員。效率備忘:題目「先篩選、再 merge」的順序是好習慣——先把 9 筆砍成 7 筆再對名單,資料量大時省時省記憶體。

相關名詞:SQL 對照一對多展開how:用哪種方式

07四個選項收工

Aamount 至少 1000,且 customer_id 能對到 active 客戶的訂單,並帶入 region正確

三段敘述對三段程式,逐字命中:「至少 1000」>= 1000(含等號——實跑 O4 的 1000 過關、O5 的 999 出局);「對到 active 客戶」merge(active[...], how='inner')(實跑刷掉 inactive 的 O3、O7 與孤兒單 O8);「帶入 region」= merge 的搬運工身分(result 四欄的最後一欄)。實跑 result:O1、O4、O6、O9 共 4 筆

B所有訂單都保留;對不到客戶者的 region 填入 activehow 與填值雙錯

inner 對不到就整列刷掉——「所有訂單都保留」直接與 how='inner' 矛盾(實跑 9 筆只剩 4 筆)。就算改成 how='left' 也只有 7 筆(關卡一刷掉的回不來),且對不到的 region 是 NaN(實跑 O3、O7、O8 三格全是 NaN)——pandas 不會把缺格填成 'active':那是 status 欄的值,跟 region 欄無關。B 把 how、填值、欄位三件事全講錯。

C只保留 amount 小於 1000 的 inactive 客戶訂單全反,且是空集合

兩個條件都跟程式相反(< 對 >=、inactive 對 active)。更妙的是把 C 描述的集合真的算出來:小額單只有 O2(800)與 O5(999),主人 C1、C3 都是 active——inactive 名單對不到任何一筆,實跑 0 筆、空集合。連反例都懶得存在的選項。

D只保留 customers 中沒有任何訂單的 active 客戶那是 anti-join 的工作

「沒有訂單的客戶」要用反向合併找:active[~active['customer_id'].isin(orders['customer_id'])]——實跑只有 C6 一位。而題目的 inner merge 是「訂單 × 名單」的交集:每一列 result 都必然掛著一筆訂單,沒有訂單的客戶在數學上就進不來(實跑 result 查無 C6)。D 描述的正好是 inner 的補集方向——考場常拿來釣「把 join 想成名單管理」的人。

回到題目:現在再作答一次

再看一次同四行程式。這次你手上有口訣了:篩金額(含等號)、對名單(inner 取交集)、region 是帶進來的章

active = customers.query("status == 'active'")   # 名單:C1/C3/C4/C6
result = (orders[orders['amount'] >= 1000]          # 關卡一:9 → 7
          .merge(active[['customer_id', 'region']],   # 帶 key 與 region
                 on='customer_id', how='inner'))      # 關卡二:7 → 4

08自我檢測

八題,全部都是本題的延伸。答錯會直接告訴你錯在哪。

09重點整理

  1. 四行程式兩件事:第 1 行 query 印活躍名單(6 位 → 4 位);第 2–4 行讓訂單過兩道關卡——金額篩選、inner 對名單——存進 result。
  2. 正確答案 A:amount 至少 1000(>= 含等號)、且 customer_id 對得到 active 客戶(inner 交集)的訂單,並帶入 region(merge 的搬運工身分)。
  3. 兩種篩選法(實跑相同)query("status == 'active'")customers[customers['status']=='active']——題目一行一種寫法,考你認出它們是同一件事。
  4. 關卡一實跑:9 筆 → 7 筆——O2(800)、O5(999)出局;O4 的 1000 整整好過關——「至少」的等號值一筆資料。
  5. 關卡二實跑:7 筆 → 4 筆——刷掉 O3(5000 元但 C2 inactive)、O7(C5 inactive)、O8(C7 不在客戶表的孤兒單)——金額再大,名單對不到就出局。
  6. result 長相:O1/O4/O6/O9,欄位 order_id / customer_id / amount / region——region 從客戶名單帶進來;status 沒跟來(第 3 行只挑了兩欄)。
  7. B 的死因(實跑):inner 對不到=整列刷掉;就算 how='left' 也只有 7 筆、對不到的 region 是 NaN——pandas 不會把缺格填成 'active'。
  8. C 的死因(實跑):條件全反之外,它描述的集合實跑是 0 筆——小額單 O2、O5 的主人都是 active。
  9. D 的死因(實跑):「沒訂單的 active 客戶」是 anti-join 的工作(實跑只有 C6);inner 的每一列都掛著訂單——C6 在數學上進不了 result
  10. 四種 how(實跑筆數):inner 4、left 7(NaN 3 格)、outer 8(C6 以空訂單現身)——對不到的下場:inner 刷掉、其他補 NaN。
  11. 一對多陷阱(實跑):右表 key 重複,左表列會複製展開——名單多塞一個 C1,result 從 4 筆變 6 筆;merge 前先驗 is_unique,金額才不會默默翻倍。
  12. 考場口訣篩金額、對名單、inner 取交集——兩關都過才留下;對不到:inner 刷掉、left 補 NaN
完整程式碼