經過昨天的 Transform 階段,我們的「訂單主表 (orders)」已經擁有了乾淨的時間格式與體質。
但你如果仔細看,表裡除了時間和狀態,就只剩下 order_id (訂單編號) 和 customer_id (客戶編號) 這類毫無溫度的英數亂碼。
如果營運長問:「我們的主力消費客群都住在哪個城市?」光看訂單表是找不到答案的。
我們必須將訂單表與「客戶表 (customers)」拼湊在一起。
今天,我們將實作 ETL 最核心的技法:跨表合併 (Merge)。
更重要的是,分享一些實戰在合併後一定會做的「資料粒度驗證」,確保資料不會在合併過程中發生災難性的問題!
在進行任何合併之前,第一步永遠是「找橋樑」。
回憶一下我們在 Day 2 看過的 ER Diagram(實體關聯圖),
orders 表和 customers 表是透過哪個欄位連線的?
沒錯,就是 customer_id。
我們先把放在字典裡的客戶表抓出來,看看裡面藏了什麼寶藏:
# 從字典中取出客戶表
customers = olist_db['olist_customers_dataset']
# 檢視客戶表的前三筆資料
customers.head(3)
實戰區 :
在 Pandas 中,處理跨表合併最強大的武器是 pd.merge()。
在實務上,我們強烈建議新手養成使用 how='left'(左外部合併)的習慣。
這代表我們要「以左邊的表(訂單表)為絕對基準,去右邊的表(客戶表)查資料並貼過來」。
為什麼不用預設的 Inner Join?
因為如果遇到極端狀況:某筆訂單在系統裡剛好找不到對應的客戶資料,
Left Join 依然會保留這筆訂單(地理資訊會填上 NaN),
而 Inner Join 則會直接把這筆訂單刪除,導致公司總營收的計算發生短少!
# 以 orders 為左表,customers 為右表,透過 'customer_id' 進行 Left Join
orders_customers = pd.merge(
orders,
customers,
on='customer_id',
how='left'
)
# 看看合併後的成果
orders_customers.head(3)
實作區 : (示意圖)

在資料工程中,講究「資料粒度 (Data Granularity)」——也就是每一列 (Row) 資料代表什麼意思。
合併前,orders 表的粒度是「一筆訂單」。
由於一筆訂單只會對應到一個送貨地址(一對一),因此合併後的 orders_customers 表,粒度應該維持不變,總筆數必須完全一樣。
如果合併後資料筆數暴增,就代表你的關聯邏輯寫錯,產生了「笛卡爾積」,後續算出來的營收全都會嚴重灌水。
因此,專業工程師合併完的第一件事,就是用程式寫下防呆驗證:
# 驗證合併前後的資料總筆數是否一致
print(f"合併前 (訂單表) 筆數: {len(orders)}")
print(f"合併後 (大表) 筆數: {len(orders_customers)}")
# 用 assert 寫一個自動防呆機制 (如果不相等,程式會報錯並停止)
assert len(orders) == len(orders_customers), "警告:資料粒度發生改變,合併後筆數異常!"
print("驗證通過:資料粒度維持不變,合併安全!")
實作區:
透過這個簡單的 assert 比對,就不必擔心資料在 ETL 管線中悄悄變形。
今天解鎖了資料分析最重要的能力之一:跨表合併 (Merge)。
我們成功利用 customer_id 作為橋樑,把買家的地理資訊完美串接到訂單上。
更重要的是,我們導入了「資料粒度」的觀念,並實作了合併前後的筆數防呆驗證,展現了資料工程思維。
明天 將要 迎接一個更進階的挑戰:把「訂單明細表 (order_items)」也合併進來。
這一次,資料的粒度可就真的會發生改變了喔!
我們 Day 7 見!