題目概述
LeetCode 第 1174 題「Immediate Food Delivery II」的目標是計算所有顧客的第一筆訂單中,即時配送(即訂單日期與顧客偏好配送日期相同)的比例,並將結果四捨五入至小數點後兩位。[問題連結]
Input 格式
資料表名為 Delivery
,包含以下欄位:
每筆記錄代表一筆訂單,顧客可以在下單時指定偏好配送日期。
delivery_id
是主鍵。Output 格式
輸出結果需包含以下欄位:
代表顧客中第一筆訂單為即時配送的比例(百分比表示法)。
總共 4 位顧客,2 位為即時 → 比例為 2/4 = 0.5
→ 50.00
Pandas 解法 1
用 idxmin
精準定位最早訂單
Step 1: 使用 groupby
搭配 idxmin()
,根據 customer_id
分組,找出每組中 order_date
最早的索引,進而取得每位顧客的第一筆訂單。
Step 2: 判斷即時配送,比較 order_date
與 customer_pref_delivery_date
是否相同,若相同則為即時配送。
Step 3: 計算百分比,統計即時配送的筆數,除以總顧客數,乘以 100,並四捨五入至小數點後兩位。
def immediate_food_delivery(delivery: pd.DataFrame) -> pd.DataFrame:
first_orders = delivery.loc[delivery.groupby('customer_id')['order_date'].idxmin()]
immediate_count = (first_orders['order_date'] == first_orders['customer_pref_delivery_date']).sum()
total_customers = first_orders.shape[0]
percentage = round(immediate_count * 100 / total_customers, 2)
return pd.DataFrame({'immediate_percentage': [percentage]})
Pandas 解法 2
排好順序直接抽第一筆
Step 1: 排序資料, 將資料依照 customer_id
和 order_date
進行排序,確保每位顧客的第一筆訂單位於最前面。
Step 2: 去除重複值,使用 drop_duplicates
,以 customer_id
為依據,保留每位顧客的第一筆訂單。
Step 3:判斷即時配送並計算百分比,同解法一。
def immediate_food_delivery(delivery: pd.DataFrame) -> pd.DataFrame:
delivery_sorted = delivery.sort_values(by=['customer_id', 'order_date'])
first_orders = delivery_sorted.drop_duplicates(subset='customer_id', keep='first')
immediate_count = (first_orders['order_date'] == first_orders['customer_pref_delivery_date']).sum()
total_customers = first_orders.shape[0]
percentage = round(immediate_count * 100 / total_customers, 2)
return pd.DataFrame({'immediate_percentage': [percentage]})
Pandas 解法 2.a
使用 first()
,直接取得每位顧客的第一筆訂單。
first_orders = delivery_sorted.groupby('customer_id').first().reset_index()
Pandas 解法 2.b
使用 nth(0)
,直接取得每位顧客的第一筆訂單。
first_orders = delivery_sorted.groupby('customer_id').nth(0).reset_index()
Pandas 解法 2.c
用 rank(method='dense')
標記首筆資料
delivery['rnk'] = delivery.groupby('customer_id')['order_date'].rank(method='dense')
first_orders = delivery[delivery['rnk'] == 1]
Pandas 解法 3
使用groupby
、merge
與agg
的組合技
Step 1: 找出每位顧客的最早訂單日期,使用 groupby
搭配 agg()
,取得每位顧客的最早 order_date
。
Step 2:合併原始資料,將上述結果與原始資料進行合併,取得完整的第一筆訂單資訊。
Step 3:標記即時配送,新增一個 immediate
欄位,若 order_date
與 customer_pref_delivery_date
相同,則標記為 1,否則為 0。
Step 4:計算百分比,
agg()
計算即時配送的總數與總配送數,agg()
回傳的是 Series,為了能像表格那樣操作,用 .to_frame()
轉成單欄 DataFrame,再 .T
做轉置,讓每個欄位在橫向呈現。def immediate_food_delivery(delivery: pd.DataFrame) -> pd.DataFrame:
first_order = delivery.groupby('customer_id', as_index=False).agg({'order_date': 'min'})
first_order_info = first_order.merge(delivery, on=['customer_id', 'order_date'])
first_order_info['immediate'] = (first_order_info['order_date'] == first_order_info['customer_pref_delivery_date']).astype(int)
df = first_order_info.agg({'immediate': 'sum', 'customer_id': 'nunique'}).to_frame().T
df['immediate_percentage'] = (df['immediate'] / df['customer_id']) * 100
return df[['immediate_percentage']].round(2)
Pandas 錯誤解法
以下解法看似快速且雖能通過偵錯,但其實不是正確的方法,你可以從第一步發現,對整張表做 .min()
,等於是對每一個欄位分別取最小值,因此它們可能來自不同訂單。只是剛好在這個例子的資料集order_date
& customer_pref_delivery_date
剛好不會被錯置,意味著這題的資料客戶想要的時間"絕對"不會比下一筆訂單(或配送單)晚。其實你仔細看看回傳的資料就可以發現customer id 3的資料已經有出了問題,回傳的delivery id是4,不是原資料的5,因為這個指令是回傳每個欄位的最小值,不過因為這題delivery id只是迷惑答題人的欄位而已,因此不會在偵錯時被擋住。
def immediate_food_delivery(delivery: pd.DataFrame) -> pd.DataFrame:
Total = len(df:= delivery.groupby("customer_id").min())
Immediate = len(df[df.order_date == df.customer_pref_delivery_date])
return pd.DataFrame({"immediate_percentage": [Immediate / Total *100]}).round(2)
謝謝您花時間將此篇文章讀完,若覺得對您有幫助可以幫忙按個讚、分享來或是珍藏喔!也歡迎Follow我的Threads/ FB,持續追蹤生產力工具、商業分析、商業英文的實用範例,提升自己的職場力喔!