昨天我們取出最重要的「訂單主表 (orders)」做了一次徹底的健康檢查。
我們成功抓出了今天必須動手修理的兩個問題:
假的時間: 系統把時間當成了普通文字。
合理的留白 (空值): 「送達時間」有 2965 個空缺。
今天,我們正式進入 ETL 流程中的 Transform(資料轉換與清洗) 階段。
我們要把假的時間變回真時間,並用實際的數據來證實昨天提到的邏輯!
在訂單主表中,總共有 5 個與時間相關的欄位(例如:下單時間、付款時間、發貨時間等)。
也許按照初學者的直覺,可能會連寫 5 行 pd.to_datetime() 來轉換格式,但這又違背了我們在 Day 3 提到的 DRY (Don't Repeat Yourself) 原則。
身為講究效率的實戰派,我把這些欄位名稱裝進一個 List,用一個簡單的 for 迴圈就能優雅解決:
# 1. 找出所有需要轉換的時間欄位名稱
time_columns = [
'order_purchase_timestamp', # 購買時間
'order_approved_at', # 付款確認時間
'order_delivered_carrier_date', # 物流發貨時間
'order_delivered_customer_date', # 送達顧客時間
'order_estimated_delivery_date' # 預計送達時間
]
# 2. 用一個迴圈,把文字字串批次轉換為真正的 datetime 格式
for col in time_columns:
orders[col] = pd.to_datetime(orders[col])
# 3. 再次檢查,確認轉換成功
orders.info()
實作圖 :

太好了!現在它們是真正的「時間」了。
未來我們就能輕鬆拿「送達時間」減去「下單時間」,精準算出一件包裹到底在路上漂流了幾天。
時間格式搞定後,我們要來面對那將近 2965 個沒有「送達顧客時間 (order_delivered_customer_date)」的空值。
昨天我們用網購的常理推斷:「如果訂單被取消,包裹沒出貨,自然就不會有送達時間。」
現在,我們用 Pandas 來驗證這個假設。
我們把那 2965 筆缺少送達時間的訂單抓出來,看看它們的「訂單狀態 (order_status)」到底是什麼?
# 1. 篩選出「沒有送達時間」的訂單
missing_delivery = orders[orders['order_delivered_customer_date'].isnull()]
# 2. 統計這些空值訂單的「訂單狀態」數量
print(missing_delivery['order_status'].value_counts())
實作圖 :

真相大白! 從結果可以看出,這些沒有送達時間的訂單,絕大多數的狀態是:
這證明了我們的判斷完全正確:這些空值不是系統出錯,而是現實世界中本來就會發生的正常狀況!
我的結論是:絕對不要用 dropna() 把這整行資料刪掉!
如果你為了畫面乾淨把它們刪了,老闆明天問你:「這個月有多少訂單被取消?退貨率是多少?」
你的數據就會少算這 2965 筆資料,導致嚴重的分析問題。
在 Pandas 的世界裡,針對時間欄位的空值,最好的處理方式就是「保留原樣」。
轉換格式後,Pandas 會自動把它們顯示為 NaT (Not a Time)。
我們要計算「物流平均天數」時,把這些 NaT 排除就好。
我們要分析「訂單取消率」時,這 2965 筆又是不可或缺的重要數據。
今天我們完成了兩件非常重要的 Transform 任務:
釐清空值邏輯:我們沒有盲目刪除空值,而是透過數據佐證,成功保留了代表「訂單取消」或「運送中」的紀錄。
現在,最重要的「訂單主表」已經被我們洗得乾乾淨淨了。
但光有骨架還不夠,明天我們將拿出另一張表,並實作資料庫分析中最經典的 JOIN(合併) 技巧,把訂單與客戶資料串聯起來!