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 10

合併資料表

merge 的四種方式、concat,鍵重複時列數暴增
Interaction實作 30 minute(s)

Learning Objectives

  • 用 merge 依共同的鍵把兩張資料表接起來,說出 inner、left、right、outer 各留下哪些列
  • 用 indicator 找出對不到的鍵,並說明合併後為什麼會出現缺值
  • 預估合併後的列數,認出鍵重複造成的列數暴增,並用 validate 提早發現
  • 用 concat 把欄位相同的資料表上下接起來

資料分散在好幾張表

上一課最後留了一個問題:成績表 grades.csv 只有學號、課程與分數,沒有系所;系所記在學生表 students.csv。 想算「各系的期末平均」,就要先把兩張表依學號接起來。

資料分成好幾張表是常態,原因很實際:學生的系所只要記一次,不必在他修的每一門課都抄一遍(抄很多次就有抄錯的機會)。 分析時再依共同的欄位把需要的表接起來,這個動作叫合併(merge),共同的欄位叫鍵(key)。

這一課用到四份檔案:grades.csv(600 筆修課成績)、students.csv(200 位學生)、memberships.csv(258 筆社團參加紀錄)、clubs.csv(10 個社團)。 它們都是程式產生的模擬資料,不是任何學校的真實資料。

逐步看:四種合併方式

左表有 S1~S4 四位學生的系所,右表有 S1、S3、S5 的期末成績。兩張表都有的學號只有 S1 與 S3。每一步看右邊的結果表多了或少了哪幾列。

四種方式怎麼選

how 留下哪些列 對不到的格子
inner(預設) 兩邊都有的鍵 沒有,對不到的列直接消失
left 左表的每一列 右表的欄補 NaN
right 右表的每一列 左表的欄補 NaN
outer 兩邊所有的鍵 兩邊都可能補 NaN

實務上最常用的是 left:「以成績表為主,把系所補上去」。選 inner 的時候要特別小心,因為對不到的列會靜悄悄地消失, 程式不會警告你。合併完先比一比列數,再用 indicator=True 看有沒有 left_only 或 right_only,是發現問題最快的方法。

兩張表的鍵欄名稱不一樣時(例如左表叫 student_id、右表叫 學號),用 left_on="student_id", right_on="學號" 分別指定。

互動練習:合併兩張小表

左表是社員名單,右表是社團表,鍵是「社團代碼」。選一種合併方式按「合併」,對到的列會用同一個顏色標出來;照下面的任務做一遍。

逐步看:把系所接到成績表上

回到上一課的問題。注意合併前後的 shape:以成績表為主做 left 合併,列數應該不變。

逐步看:對不到的鍵,以及沒參加社團的人

社員紀錄裡有兩個社團代碼(C11、C12)不在社團表裡,可能是新成立還沒登記的社團。看 indicator 怎麼把它們找出來。

鍵重複時,列數會相乘

合併時,左表的一列會和右表每一個鍵相同的列配成一對。所以鍵在兩邊都重複時,列數是相乘的: 左表有 2 列 S1、右表有 3 列 S1,合併後就有 2 × 3 = 6 列 S1。這叫多對多(many-to-many)合併, 多半是忘了先彙總或選錯了鍵,結果算平均、總和時同一筆資料被重複計算。

合併前先想清楚兩張表的關係:

關係 例子 validate=
一對一 學生基本資料 × 學生聯絡資料 "one_to_one"
多對一 成績(一人多筆)× 學生(一人一筆) "many_to_one"
多對多 社團紀錄(一人多筆)× 成績(一人多筆) 通常是錯誤

把預期的關係寫進 validate,不符合時 pandas 會丟出 MergeError,比事後發現數字不對容易得多。

逐步看:多對多合併讓列數暴增

先看一個 3 列和 4 列的小例子,再看社團紀錄直接和成績表合併會發生什麼事。

concat:欄位相同的表上下接起來

merge 是左右接(依鍵配對,欄變多);pd.concat 最常見的用法是上下接(列變多),例如把上學期和下學期的成績表接成一張。

fall = pd.DataFrame({"學號": ["S1", "S2"], "期末": [82, 75]})
spring = pd.DataFrame({"學號": ["S1", "S3"], "期末": [88, 91]})
both = pd.concat([fall, spring], ignore_index=True)   # 4 列,索引重新編為 0~3

兩件事要注意: - 不加 ignore_index=True 時,原本的索引會照抄,結果的索引是 0、1、0、1,有重複。 - 欄位名稱要一致:一張叫「期末」、另一張叫「final」,concat 會當成兩個不同的欄,各自補上 NaN。

實作:各類社團的參加人次

讀入 memberships.csv 與 clubs.csv,依 club_id 合併(用預設的 inner),再依社團類別(category)分組, 用 size() 算出每一類的參加人次,由多到少排序後印出。(對不到的 C11、C12 會被 inner 排除,這一題就是要這樣算。)

你的輸出
預期輸出

小測驗:合併方式

1.

成績表有 600 列(一人可能有好幾列),學生表有 200 列(每位學生剛好一列,而且成績表的學號都在學生表裡)。grades.merge(students, on="student_id", how="left") 的結果有幾列?

2.

想保留兩張表所有的鍵,對不到的地方補 NaN,how 要設成什麼?

3.

indicator=True 產生的 _merge 欄裡,某一列是 left_only,代表什麼?

第 1 次作答

小測驗:列數與 concat

1.

下面程式會印出什麼?

import pandas as pd
x = pd.DataFrame({"k": ["a", "a", "b"], "v": [1, 2, 3]})
y = pd.DataFrame({"k": ["a", "a", "c"], "w": [7, 8, 9]})
print(len(x.merge(y, on="k")), len(x.merge(y, on="k", how="outer")))
2.

grades.merge(students, on="student_id", validate="many_to_one") 在什麼情況下會丟出 MergeError?

3.

下面程式會印出什麼?

import pandas as pd
a = pd.DataFrame({"id": [1, 2]})
b = pd.DataFrame({"id": [3]})
print(pd.concat([a, b]).index.tolist())
第 1 次作答

重點整理

  • pd.merge(左, 右, on="鍵", how=...):依共同的鍵把兩張表左右接起來;鍵名不同時用 left_on、right_on。
  • inner 只留兩邊都有的鍵(預設),left、right 保留其中一邊的每一列,outer 全部保留;對不到的格子是 NaN,整數欄會因此變成浮點數。
  • 合併後先比列數,再用 indicator=True 看有沒有 left_only、right_only;inner 會讓對不到的列靜悄悄消失。
  • 鍵在兩邊都重複時列數相乘;用 validate="many_to_one" 等寫下預期的關係,讓錯誤提早出現。
  • pd.concat 把欄位相同的表上下接起來,記得 ignore_index=True。
  • 下一課處理資料表的形狀:同一份成績可以是「一列一筆修課」的長表,也可以是「一列一位學生、一欄一門課」的寬表,兩種形狀怎麼互轉、各適合做什麼。
這一課完成了!
Next lesson:長表與寬表
你目前沒有登入,學習進度只存在這個瀏覽器。 Login 後,進度會跟著帳號,換電腦也看得到。