diff --git a/homework/m1_numpy.py b/homework/m1_numpy.py index 5ff41ab..1d954ac 100644 --- a/homework/m1_numpy.py +++ b/homework/m1_numpy.py @@ -1,80 +1,75 @@ -""" -M1 NumPy 向量化思維 — 課後作業 -================================ -請完成以下每個函式,用 NumPy 向量化寫法(不要 for-loop)。 -完成後 git push,GitHub Actions 會自動批改並顯示成績與解答。 - -提示: -- np.array, np.where, np.argsort -- 布林遮罩: arr[arr > 10] -- 統計: .sum(), .mean(), .max(), .min() -""" -import numpy as np - - -# ============================================================ -# 🟢 送分題(每題 10 分,共 30 分) -# ============================================================ - -def green_mean(): - """建立 [10, 20, 30, 40, 50],回傳所有元素的平均值 (float)""" - # TODO: 你的程式碼 - pass - - -def green_double(): - """建立 [10, 20, 30, 40, 50],回傳所有元素乘以 2 的 ndarray""" - # TODO: 你的程式碼 - pass - - -def green_filter(): - """建立 [10, 20, 30, 40, 50],回傳大於 25 的元素 (ndarray)""" - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🟡 核心題(每題 15 分,共 45 分) -# 以下函式會接收從 products.csv 讀出的 prices, stocks 陣列 -# ============================================================ - -def yellow_expensive_count(prices): - """回傳單價 > 1000 的商品數量 (int)""" - # TODO: 你的程式碼 - pass - - -def yellow_top3_stock_indices(stocks): - """ - 回傳庫存最多的前 3 個商品的索引位置 (ndarray, 由大到小排) - 提示:np.argsort - """ - # TODO: 你的程式碼 - pass - - -def yellow_restock_cost(prices, stocks): - """ - 單價 < 500 的商品,每種各進貨 50 個,回傳總花費 (float/int) - 提示:布林遮罩 + .sum() - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🔴 挑戰題(25 分) -# ============================================================ - -def red_double11_prices(prices, stocks): - """ - 雙 11 定價規則(必須向量化,不能用 for-loop): - - 庫存 >= 100:打 7 折 - - 庫存 20~99:打 9 折 - - 庫存 < 20:原價 - 回傳每個商品的雙 11 售價 (ndarray) - 提示:np.where 可以巢狀使用 - """ - # TODO: 你的程式碼 - pass +""" +M1 NumPy 向量化思維 — 課後作業 +================================ +請完成以下每個函式,用 NumPy 向量化寫法(不要 for-loop)。 +完成後 git push,GitHub Actions 會自動批改並顯示成績與解答。 + +提示: +- np.array, np.where, np.argsort +- 布林遮罩: arr[arr > 10] +- 統計: .sum(), .mean(), .max(), .min() +""" +import numpy as np + + +# ============================================================ +# 🟢 送分題(每題 10 分,共 30 分) +# ============================================================ + +def green_mean(): + """建立 [10, 20, 30, 40, 50],回傳所有元素的平均值 (float)""" + arr = np.array([10, 20, 30, 40, 50]) + return arr.mean() + + + +def green_double(): + """建立 [10, 20, 30, 40, 50],回傳所有元素乘以 2 的 ndarray""" + arr = np.array([10, 20, 30, 40, 50]) + return arr*2 + +def green_filter(): + """建立 [10, 20, 30, 40, 50],回傳大於 25 的元素 (ndarray)""" + arr = np.array([10, 20, 30, 40, 50]) + return arr[arr > 25] + + +# ============================================================ +# 🟡 核心題(每題 15 分,共 45 分) +# 以下函式會接收從 products.csv 讀出的 prices, stocks 陣列 +# ============================================================ + +def yellow_expensive_count(prices): + """回傳單價 > 1000 的商品數量 (int)""" + return (prices > 1000).sum + + +def yellow_top3_stock_indices(stocks): + """ + 回傳庫存最多的前 3 個商品的索引位置 (ndarray, 由大到小排) + 提示:np.argsort + """ + return np.argsort(stocks)[-3:][::-1] + + +def yellow_restock_cost(prices, stocks): + """ + 單價 < 500 的商品,每種各進貨 50 個,回傳總花費 (float/int) + 提示:布林遮罩 + .sum() + """ + return (prices[prices < 500] * 50).sum() + +# ============================================================ +# 🔴 挑戰題(25 分) +# ============================================================ + +def red_double11_prices(prices, stocks): + """ + 雙 11 定價規則(必須向量化,不能用 for-loop): + - 庫存 >= 100:打 7 折 + - 庫存 20~99:打 9 折 + - 庫存 < 20:原價 + 回傳每個商品的雙 11 售價 (ndarray) + 提示:np.where 可以巢狀使用 + """ + return np.where(stocks >= 100, prices * 0.7, np.where(stocks >= 20, prices * 0.9, prices)) diff --git a/homework/m2_pandas_cleaning.py b/homework/m2_pandas_cleaning.py index fa36bff..6a6007b 100644 --- a/homework/m2_pandas_cleaning.py +++ b/homework/m2_pandas_cleaning.py @@ -1,96 +1,111 @@ -""" -M2 Pandas I/O 與資料清理 — 課後作業 -==================================== -情境:你拿到一份「故意弄髒」的訂單 CSV (orders_raw.csv), -裡面有欄位名稱空格、金額帶 $ 符號、日期格式錯誤、缺值、重複列。 -請用 Pandas 把它清乾淨。 - -資料路徑:datasets/ecommerce/orders_raw.csv -""" -import pandas as pd - - -# ============================================================ -# 🟢 送分題(每題 10 分,共 30 分) -# ============================================================ - -def green_read_csv(): - """ - 讀取 orders_raw.csv,回傳原始 DataFrame(不做任何清理) - 提示:pd.read_csv() - """ - # TODO: 你的程式碼 - pass - - -def green_shape(df): - """ - 回傳 DataFrame 的 (列數, 欄數) tuple - 提示:df.shape - """ - # TODO: 你的程式碼 - pass - - -def green_dtypes(df): - """ - 回傳 DataFrame 的欄位型別 (Series) - 提示:df.dtypes - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🟡 核心題(每題 15 分,共 45 分) -# ============================================================ - -def yellow_clean_columns(df): - """ - 清理欄位名稱:去除前後空白、全部轉小寫 - 回傳清理後的 DataFrame(不要修改原始 df) - 提示:df.columns.str.strip().str.lower() - """ - # TODO: 你的程式碼 - pass - - -def yellow_clean_amount(df): - """ - 清理 amount 欄位:移除 '$' 和 ',' 符號,轉為 float - 假設欄位名稱已經是小寫的 'amount' - 回傳清理後的 DataFrame(不要修改原始 df) - 提示:.str.replace() + .astype(float) - """ - # TODO: 你的程式碼 - pass - - -def yellow_drop_duplicates(df): - """ - 移除完全重複的列,回傳去重後的 DataFrame - 提示:df.drop_duplicates() - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🔴 挑戰題(25 分) -# ============================================================ - -def red_clean_orders(path): - """ - 完整清理 pipeline:一個函式搞定所有清理步驟 - 1. 讀取 CSV - 2. 欄位名稱:去空白、轉小寫 - 3. amount:移除 '$' ',',轉 float - 4. order_date:轉為 datetime(無法轉換的設為 NaT) - 5. 刪除 amount 或 order_date 為空的列 - 6. 移除重複列 - - 回傳:清理後的 DataFrame - 提示:pd.to_datetime(errors='coerce') - """ - # TODO: 你的程式碼 - pass +""" +M2 Pandas I/O 與資料清理 — 課後作業 +==================================== +情境:你拿到一份「故意弄髒」的訂單 CSV (orders_raw.csv), +裡面有欄位名稱空格、金額帶 $ 符號、日期格式錯誤、缺值、重複列。 +請用 Pandas 把它清乾淨。 + +資料路徑:datasets/ecommerce/orders_raw.csv +""" +import pandas as pd + + +# ============================================================ +# 🟢 送分題(每題 10 分,共 30 分) +# ============================================================ + +def green_read_csv(): + """ + 讀取 orders_raw.csv,回傳原始 DataFrame(不做任何清理) + 提示:pd.read_csv() + """ + return pd.read_csv("datasets/ecommerce/orders_raw.csv") + + +def green_shape(df): + """ + 回傳 DataFrame 的 (列數, 欄數) tuple + 提示:df.shape + """ + return df.shape + + +def green_dtypes(df): + """ + 回傳 DataFrame 的欄位型別 (Series) + 提示:df.dtypes + """ + return df.dtypes + + +# ============================================================ +# 🟡 核心題(每題 15 分,共 45 分) +# ============================================================ + +def yellow_clean_columns(df): + """ + 清理欄位名稱:去除前後空白、全部轉小寫 + 回傳清理後的 DataFrame(不要修改原始 df) + 提示:df.columns.str.strip().str.lower() + """ + new_df = df.copy() + new_df.columns = new_df.columns.str.strip().str.lower() + return new_df + + +def yellow_clean_amount(df): + """ + 清理 amount 欄位:移除 '$' 和 ',' 符號,轉為 float + 假設欄位名稱已經是小寫的 'amount' + 回傳清理後的 DataFrame(不要修改原始 df) + 提示:.str.replace() + .astype(float) + """ + new_df = df.copy() + new_df['amount'] = (new_df['amount'] + .str.replace('$', '', regex=False) + .str.replace(',', '', regex=False) + .astype(float)) + return new_df + + +def yellow_drop_duplicates(df): + """ + 移除完全重複的列,回傳去重後的 DataFrame + 提示:df.drop_duplicates() + """ + return df.drop_duplicates() + +# ============================================================ +# 🔴 挑戰題(25 分) +# ============================================================ + +def red_clean_orders(path): + """ + 完整清理 pipeline:一個函式搞定所有清理步驟 + 1. 讀取 CSV + 2. 欄位名稱:去空白、轉小寫 + 3. amount:移除 '$' ',',轉 float + 4. order_date:轉為 datetime(無法轉換的設為 NaT) + 5. 刪除 amount 或 order_date 為空的列 + 6. 移除重複列 + + 回傳:清理後的 DataFrame + 提示:pd.to_datetime(errors='coerce') + """ +def yellow_clean_columns(df): + new_df = df.copy() + new_df.columns = new_df.columns.str.strip().str.lower() + return new_df + + +def yellow_clean_amount(df): + new_df = df.copy() + new_df['amount'] = (new_df['amount'] + .str.replace('$', '', regex=False) + .str.replace(',', '', regex=False) + .astype(float)) + return new_df + + +def yellow_drop_duplicates(df): + return df.drop_duplicates() \ No newline at end of file diff --git a/homework/m3_pandas_advanced.py b/homework/m3_pandas_advanced.py index 68410e8..3674423 100644 --- a/homework/m3_pandas_advanced.py +++ b/homework/m3_pandas_advanced.py @@ -1,97 +1,103 @@ -""" -M3 Pandas 進階:merge / groupby / RFM — 課後作業 -================================================= -情境:你已經有清理好的訂單資料,現在要合併客戶和商品表, -做出有商業價值的分析。 - -資料路徑: - - datasets/ecommerce/orders_clean.csv - - datasets/ecommerce/customers.csv - - datasets/ecommerce/products.csv -""" -import pandas as pd - - -# ============================================================ -# 🟢 送分題(每題 10 分,共 30 分) -# ============================================================ - -def green_load_and_merge(): - """ - 讀取三張表,合併成一張完整的 DataFrame 並回傳 - - orders_clean.csv LEFT JOIN customers.csv ON customer_id - - 再 LEFT JOIN products.csv ON product_id - 提示:pd.merge(how='left') - """ - # TODO: 你的程式碼 - pass - - -def green_row_count(df): - """回傳 DataFrame 的列數 (int)""" - # TODO: 你的程式碼 - pass - - -def green_column_list(df): - """回傳 DataFrame 的所有欄位名稱 (list)""" - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🟡 核心題(每題 15 分,共 45 分) -# ============================================================ - -def yellow_top_category(df): - """ - 哪個商品類別 (category) 的總營收最高? - 回傳該類別名稱 (str) - 提示:groupby('category')['amount'].sum() - """ - # TODO: 你的程式碼 - pass - - -def yellow_gold_vip_stats(df): - """ - Gold VIP 客戶總共下了幾張訂單?總金額多少? - 回傳 tuple: (訂單數 int, 總金額 float) - 提示:df[df['vip_level'] == 'Gold'] - """ - # TODO: 你的程式碼 - pass - - -def yellow_region_avg_amount(df): - """ - 計算每個地區 (region) 的平均訂單金額 - 回傳 Series(index=region, values=平均金額) - 提示:groupby('region')['amount'].mean() - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🔴 挑戰題(25 分) -# ============================================================ - -def red_rfm_top5(df): - """ - RFM 分析:找出最有價值的前 5 位客戶 - - 計算每位客戶的: - - R (Recency):最近一次下單日期 - - F (Frequency):訂單總數 - - M (Monetary):消費總金額 - - 回傳 DataFrame: - - 欄位:customer_id, customer_name, R, F, M - - 按 M 由大到小排序 - - 只取前 5 筆 - - 提示:groupby('customer_id').agg(...) - """ - # TODO: 你的程式碼 - pass +""" +M3 Pandas 進階:merge / groupby / RFM — 課後作業 +================================================= +情境:你已經有清理好的訂單資料,現在要合併客戶和商品表, +做出有商業價值的分析。 + +資料路徑: + - datasets/ecommerce/orders_clean.csv + - datasets/ecommerce/customers.csv + - datasets/ecommerce/products.csv +""" +import pandas as pd + + +# ============================================================ +# 🟢 送分題(每題 10 分,共 30 分) +# ============================================================ + +def green_load_and_merge(): + """ + 讀取三張表,合併成一張完整的 DataFrame 並回傳 + - orders_clean.csv LEFT JOIN customers.csv ON customer_id + - 再 LEFT JOIN products.csv ON product_id + 提示:pd.merge(how='left') + """ + orders = pd.read_csv('../datasets/ecommerce/orders_clean.csv') + customers = pd.read_csv('../datasets/ecommerce/customers.csv') + products = pd.read_csv('../datasets/ecommerce/products.csv') + + df = pd.merge(orders, customers, on='customer_id', how='left') + df = pd.merge(df, products, on='product_id', how='left') + return df + + +def green_row_count(df): + """回傳 DataFrame 的列數 (int)""" + return len(df) + + +def green_column_list(df): + """回傳 DataFrame 的所有欄位名稱 (list)""" + return list(df.columns) + + +# ============================================================ +# 🟡 核心題(每題 15 分,共 45 分) +# ============================================================ + +def yellow_top_category(df): + """ + 哪個商品類別 (category) 的總營收最高? + 回傳該類別名稱 (str) + 提示:groupby('category')['amount'].sum() + """ + return df.groupby('category')['amount'].sum().idxmax() + + +def yellow_gold_vip_stats(df): + """ + Gold VIP 客戶總共下了幾張訂單?總金額多少? + 回傳 tuple: (訂單數 int, 總金額 float) + 提示:df[df['vip_level'] == 'Gold'] + """ + gold_vip = df[df['vip_level'] == 'Gold'] + return (len(gold_vip), float(gold_vip['amount'].sum())) + +def yellow_region_avg_amount(df): + """ + 計算每個地區 (region) 的平均訂單金額 + 回傳 Series(index=region, values=平均金額) + 提示:groupby('region')['amount'].mean() + """ + return df.groupby('region')['amount'].mean() + + +# ============================================================ +# 🔴 挑戰題(25 分) +# ============================================================ + +def red_rfm_top5(df): + """ + RFM 分析:找出最有價值的前 5 位客戶 + + 計算每位客戶的: + - R (Recency):最近一次下單日期 + - F (Frequency):訂單總數 + - M (Monetary):消費總金額 + + 回傳 DataFrame: + - 欄位:customer_id, customer_name, R, F, M + - 按 M 由大到小排序 + - 只取前 5 筆 + + 提示:groupby('customer_id').agg(...) + """ + rfm = df.groupby(['customer_id', 'customer_name']).agg({ + 'order_date': 'max', + 'order_id': 'count', + 'amount': 'sum' + }).reset_index() + + rfm.columns = ['customer_id', 'customer_name', 'R', 'F', 'M'] + return rfm.sort_values('M', ascending=False).head(5) \ No newline at end of file diff --git a/homework/m4_timeseries.py b/homework/m4_timeseries.py index 047af6a..43fccce 100644 --- a/homework/m4_timeseries.py +++ b/homework/m4_timeseries.py @@ -1,104 +1,108 @@ -""" -M4 時間序列與 EDA — 課後作業 -============================== -情境:用合併好的訂單資料做時間維度分析, -產出月報級別的商業洞察。 - -資料路徑:datasets/ecommerce/orders_enriched.csv -""" -import pandas as pd - - -def _load_data(): - """輔助函式:讀取並解析日期""" - df = pd.read_csv("datasets/ecommerce/orders_enriched.csv", - parse_dates=["order_date"]) - return df - - -# ============================================================ -# 🟢 送分題(每題 10 分,共 30 分) -# ============================================================ - -def green_avg_by_month(): - """ - 計算每個月份 (1~12) 的平均訂單金額 - 回傳 Series(index=月份 1~12, values=平均金額) - 提示:df['order_date'].dt.month - """ - # TODO: 你的程式碼 - pass - - -def green_top3_dates(): - """ - 找出訂單數最多的前 3 個日期 - 回傳 Series(index=日期, values=訂單數, 由多到少排序) - 提示:value_counts().head(3) - """ - # TODO: 你的程式碼 - pass - - -def green_date_range(): - """ - 回傳資料的日期範圍 tuple: (最早日期, 最晚日期) - 格式為 pandas Timestamp - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🟡 核心題(每題 15 分,共 45 分) -# ============================================================ - -def yellow_monthly_revenue(): - """ - 計算每月總營收 - 回傳 Series(index=月底日期 period, values=總營收) - 提示:set_index('order_date').resample('ME')['amount'].sum() - """ - # TODO: 你的程式碼 - pass - - -def yellow_rolling_avg(monthly_revenue): - """ - 計算 3 個月移動平均 - 接收 yellow_monthly_revenue() 的結果作為輸入 - 回傳 Series(同樣 index,values=移動平均,前 2 筆可為 NaN) - 提示:.rolling(window=3).mean() - """ - # TODO: 你的程式碼 - pass - - -def yellow_category_median(df): - """ - 計算每個商品類別 (category) 的訂單金額中位數,由高到低排序 - 回傳 Series(index=category, values=中位數) - 提示:groupby + median + sort_values - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🔴 挑戰題(25 分) -# ============================================================ - -def red_monthly_report(): - """ - 產出月報 DataFrame,每月一列,包含: - - order_count:當月訂單數 - - revenue:當月總營收 - - active_customers:當月不重複客戶數 - - avg_order_value:客單價(revenue / order_count) - - revenue_growth:月營收成長率(相對上月的 % 變化) - - index 為月份 (period 或 datetime) - 提示:resample + agg + pct_change - """ - # TODO: 你的程式碼 - pass +""" +M4 時間序列與 EDA — 課後作業 +============================== +情境:用合併好的訂單資料做時間維度分析, +產出月報級別的商業洞察。 + +資料路徑:datasets/ecommerce/orders_enriched.csv +""" +import pandas as pd + + +def _load_data(): + """輔助函式:讀取並解析日期""" + df = pd.read_csv("datasets/ecommerce/orders_enriched.csv", + parse_dates=["order_date"]) + return df + + +# ============================================================ +# 🟢 送分題(每題 10 分,共 30 分) +# ============================================================ + +def green_avg_by_month(): + """ + 計算每個月份 (1~12) 的平均訂單金額 + 回傳 Series(index=月份 1~12, values=平均金額) + 提示:df['order_date'].dt.month + """ + return df.groupby(df['order_date'].dt.month)['amount'].mean() + + +def green_top3_dates(): + """ + 找出訂單數最多的前 3 個日期 + 回傳 Series(index=日期, values=訂單數, 由多到少排序) + 提示:value_counts().head(3) + """ + return df['order_date'].dt.date.value_counts().head(3) + + +def green_date_range(): + """ + 回傳資料的日期範圍 tuple: (最早日期, 最晚日期) + 格式為 pandas Timestamp + """ + return (df['order_date'].min(), df['order_date'].max()) + + + + +# ============================================================ +# 🟡 核心題(每題 15 分,共 45 分) +# ============================================================ + +def yellow_monthly_revenue(): + """ + 計算每月總營收 + 回傳 Series(index=月底日期 period, values=總營收) + 提示:set_index('order_date').resample('ME')['amount'].sum() + """ + return df.set_index('order_date').resample('ME')['amount'].sum() + + +def yellow_rolling_avg(monthly_revenue): + """ + 計算 3 個月移動平均 + 接收 yellow_monthly_revenue() 的結果作為輸入 + 回傳 Series(同樣 index,values=移動平均,前 2 筆可為 NaN) + 提示:.rolling(window=3).mean() + """ + return monthly_revenue.rolling(window=3).mean() + + +def yellow_category_median(df): + """ + 計算每個商品類別 (category) 的訂單金額中位數,由高到低排序 + 回傳 Series(index=category, values=中位數) + 提示:groupby + median + sort_values + """ + return df.groupby('category')['amount'].median().sort_values(ascending=False) + + +# ============================================================ +# 🔴 挑戰題(25 分) +# ============================================================ + +def red_monthly_report(): + """ + 產出月報 DataFrame,每月一列,包含: + - order_count:當月訂單數 + - revenue:當月總營收 + - active_customers:當月不重複客戶數 + - avg_order_value:客單價(revenue / order_count) + - revenue_growth:月營收成長率(相對上月的 % 變化) + + index 為月份 (period 或 datetime) + 提示:resample + agg + pct_change + """ + report = df.set_index('order_date').resample('ME').agg({ + 'amount': ['count', 'sum'], + 'customer_id': 'nunique' + }) + + report.columns = ['order_count', 'revenue', 'active_customers'] + report['avg_order_value'] = report['revenue'] / report['order_count'] + report['revenue_growth'] = report['revenue'].pct_change() + + return report diff --git a/homework/m5_visualization.py b/homework/m5_visualization.py index 7e7335d..6b2051c 100644 --- a/homework/m5_visualization.py +++ b/homework/m5_visualization.py @@ -1,110 +1,119 @@ -""" -M5 Matplotlib & Seaborn 視覺化 — 課後作業 -========================================== -情境:把分析結果做成圖表,用視覺化說故事。 - -資料路徑:datasets/ecommerce/orders_enriched.csv -""" -import matplotlib -matplotlib.use("Agg") # 無 GUI 環境也能跑 -import matplotlib.pyplot as plt -import pandas as pd -import seaborn as sns - - -def _load_data(): - """輔助函式:讀取資料""" - return pd.read_csv("datasets/ecommerce/orders_enriched.csv", - parse_dates=["order_date"]) - - -# ============================================================ -# 🟢 送分題(每題 10 分,共 30 分) -# ============================================================ - -def green_bar_category(): - """ - 畫出每個商品類別 (category) 的訂單數長條圖 - 回傳 matplotlib Figure 物件 - 提示:sns.countplot 或 value_counts().plot.bar() - """ - # TODO: 你的程式碼 - pass - - -def green_hist_amount(): - """ - 畫出訂單金額 (amount) 的分佈直方圖,分 20 個 bin - 回傳 matplotlib Figure 物件 - 提示:sns.histplot(bins=20) 或 plt.hist() - """ - # TODO: 你的程式碼 - pass - - -def green_set_labels(): - """ - 建立一個簡單的長條圖(內容不限),但必須設定: - - 圖標題 (title) - - X 軸標籤 (xlabel) - - Y 軸標籤 (ylabel) - 回傳 matplotlib Figure 物件 - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🟡 核心題(每題 15 分,共 45 分) -# ============================================================ - -def yellow_line_region_trend(): - """ - 畫折線圖:比較 North 和 South 兩個地區的月營收趨勢 - - X 軸:月份 - - Y 軸:該月總營收 - - 兩條線,有圖例 (legend) - 回傳 matplotlib Figure 物件 - 提示:分別 groupby 再 plot,或用 sns.lineplot(hue='region') - """ - # TODO: 你的程式碼 - pass - - -def yellow_box_vip(): - """ - 畫箱形圖:比較不同 VIP 等級 (vip_level) 的訂單金額分佈 - 回傳 matplotlib Figure 物件 - 提示:sns.boxplot(x='vip_level', y='amount', data=df) - """ - # TODO: 你的程式碼 - pass - - -def yellow_scatter_price_amount(): - """ - 畫散佈圖:X=商品單價 (unit_price),Y=訂單金額 (amount) - 回傳 matplotlib Figure 物件 - 提示:plt.scatter() 或 sns.scatterplot() - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🔴 挑戰題(25 分) -# ============================================================ - -def red_category_dashboard(category="Electronics"): - """ - 針對指定類別,畫 2×2 的 subplot dashboard: - 1. 左上:該類別月營收趨勢 (折線圖) - 2. 右上:該類別各地區營收 (長條圖) - 3. 左下:該類別 Top 5 商品營收 (水平長條圖) - 4. 右下:該類別訂單金額分佈 (直方圖) - - 回傳 matplotlib Figure 物件 - 提示:fig, axes = plt.subplots(2, 2, figsize=(14, 10)) - """ - # TODO: 你的程式碼 - pass +""" +M5 Matplotlib & Seaborn 視覺化 — 課後作業 +========================================== +情境:把分析結果做成圖表,用視覺化說故事。 + +資料路徑:datasets/ecommerce/orders_enriched.csv +""" +import matplotlib +matplotlib.use("Agg") # 無 GUI 環境也能跑 +import matplotlib.pyplot as plt +import pandas as pd +import seaborn as sns + + +def _load_data(): + """輔助函式:讀取資料""" + return pd.read_csv("datasets/ecommerce/orders_enriched.csv", + parse_dates=["order_date"]) + + +# ============================================================ +# 🟢 送分題(每題 10 分,共 30 分) +# ============================================================ + +def green_bar_category(): + """ + 畫出每個商品類別 (category) 的訂單數長條圖 + 回傳 matplotlib Figure 物件 + 提示:sns.countplot 或 value_counts().plot.bar() + """ + fig = plt.figure() + sns.countplot(x='category', data=df) + return fig + +def green_hist_amount(): + """ + 畫出訂單金額 (amount) 的分佈直方圖,分 20 個 bin + 回傳 matplotlib Figure 物件 + 提示:sns.histplot(bins=20) 或 plt.hist() + """ + fig = plt.figure() + sns.histplot(df['amount'], bins=20) + return fig + + +def green_set_labels(): + """ + 建立一個簡單的長條圖(內容不限),但必須設定: + - 圖標題 (title) + - X 軸標籤 (xlabel) + - Y 軸標籤 (ylabel) + 回傳 matplotlib Figure 物件 + """ + fig = plt.figure() + plt.bar(['A', 'B', 'C'], [1, 2, 3]) + plt.title("Sample Title") + plt.xlabel("X Label") + plt.ylabel("Y Label") + return fig + + +# ============================================================ +# 🟡 核心題(每題 15 分,共 45 分) +# ============================================================ + +def yellow_line_region_trend(): + """ + 畫折線圖:比較 North 和 South 兩個地區的月營收趨勢 + - X 軸:月份 + - Y 軸:該月總營收 + - 兩條線,有圖例 (legend) + 回傳 matplotlib Figure 物件 + 提示:分別 groupby 再 plot,或用 sns.lineplot(hue='region') + """ + fig = plt.figure() + plot_df = df[df['region'].isin(['North', 'South'])].copy() + plot_df['month'] = plot_df['order_date'].dt.to_period('M').astype(str) + trend = plot_df.groupby(['month', 'region'])['amount'].sum().reset_index() + sns.lineplot(data=trend, x='month', y='amount', hue='region') + plt.legend() + return fig + +def yellow_box_vip(): + """ + 畫箱形圖:比較不同 VIP 等級 (vip_level) 的訂單金額分佈 + 回傳 matplotlib Figure 物件 + 提示:sns.boxplot(x='vip_level', y='amount', data=df) + """ + fig = plt.figure() + sns.boxplot(x='vip_level', y='amount', data=df) + return fig + +def yellow_scatter_price_amount(): + """ + 畫散佈圖:X=商品單價 (unit_price),Y=訂單金額 (amount) + 回傳 matplotlib Figure 物件 + 提示:plt.scatter() 或 sns.scatterplot() + """ + fig = plt.figure() + sns.scatterplot(x='unit_price', y='amount', data=df) + return fig + + + +# ============================================================ +# 🔴 挑戰題(25 分) +# ============================================================ + +def red_category_dashboard(category="Electronics"): + """ + 針對指定類別,畫 2×2 的 subplot dashboard: + 1. 左上:該類別月營收趨勢 (折線圖) + 2. 右上:該類別各地區營收 (長條圖) + 3. 左下:該類別 Top 5 商品營收 (水平長條圖) + 4. 右下:該類別訂單金額分佈 (直方圖) + + 回傳 matplotlib Figure 物件 + 提示:fig, axes = plt.subplots(2, 2, figsize=(14, 10)) + """ diff --git a/homework/m6_plotly_capstone.py b/homework/m6_plotly_capstone.py index 0e2c32a..85f7a7b 100644 --- a/homework/m6_plotly_capstone.py +++ b/homework/m6_plotly_capstone.py @@ -1,118 +1,148 @@ -""" -M6 Plotly 互動儀表板 & Capstone — 課後作業 -=========================================== -情境:從原始資料到互動式儀表板,完成完整的資料分析 pipeline。 - -資料路徑: - - datasets/ecommerce/orders_raw.csv(原始髒資料) - - datasets/ecommerce/customers.csv - - datasets/ecommerce/products.csv -""" -import pandas as pd -import plotly.express as px -import plotly.graph_objects as go -from plotly.subplots import make_subplots - - -# ============================================================ -# 🟢 送分題(每題 10 分,共 30 分) -# ============================================================ - -def green_plotly_bar(): - """ - 用 Plotly Express 畫出每個商品類別 (category) 的總營收長條圖 - 資料來源:orders_enriched.csv - 回傳 plotly Figure 物件 - 提示:px.bar() - """ - # TODO: 你的程式碼 - pass - - -def green_plotly_line(): - """ - 用 Plotly Express 畫出月營收趨勢折線圖 - 資料來源:orders_enriched.csv - 回傳 plotly Figure 物件 - 提示:先 groupby 月份算總營收,再 px.line() - """ - # TODO: 你的程式碼 - pass - - -def green_plotly_pie(): - """ - 用 Plotly Express 畫出 VIP 等級 (vip_level) 的訂單數佔比圓餅圖 - 資料來源:orders_enriched.csv - 回傳 plotly Figure 物件 - 提示:px.pie() - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🟡 核心題(每題 15 分,共 45 分) -# ============================================================ - -def yellow_clean_and_merge(raw_path, customers_path, products_path): - """ - 完整 ETL:從髒資料到合併完成的 DataFrame - 1. 讀取 orders_raw.csv 並清理(欄位名稱、金額、日期、缺值、去重) - 2. 合併 customers.csv 和 products.csv - 回傳:合併後的 DataFrame - """ - # TODO: 你的程式碼 - pass - - -def yellow_kpi_summary(df): - """ - 計算 4 個核心 KPI,回傳 dict: - { - "total_revenue": float, # 總營收 - "order_count": int, # 訂單數 - "active_customers": int, # 不重複客戶數 - "avg_order_value": float, # 平均客單價 - } - """ - # TODO: 你的程式碼 - pass - - -def yellow_plotly_scatter(df): - """ - 用 Plotly Express 畫互動散佈圖: - - X:商品單價 (unit_price) - - Y:訂單金額 (amount) - - 顏色:商品類別 (category) - - hover 顯示:商品名稱 (product_name) - 回傳 plotly Figure 物件 - 提示:px.scatter(hover_data=['product_name']) - """ - # TODO: 你的程式碼 - pass - - -# ============================================================ -# 🔴 挑戰題(25 分) -# ============================================================ - -def red_dashboard(): - """ - Capstone:完整的互動式儀表板 - - 流程: - 1. 清理 orders_raw.csv + 合併三張表 - 2. 建立 2×2 subplot dashboard(用 plotly make_subplots): - - 左上:月營收趨勢 (line) - - 右上:Top 10 商品營收 (bar) - - 左下:各地區營收 (bar) - - 右下:類別營收佔比 (pie/donut) - 3. 設定整體標題 - - 回傳 plotly Figure 物件 - 提示:from plotly.subplots import make_subplots - """ - # TODO: 你的程式碼 - pass +""" +M6 Plotly 互動儀表板 & Capstone — 課後作業 +=========================================== +情境:從原始資料到互動式儀表板,完成完整的資料分析 pipeline。 + +資料路徑: + - datasets/ecommerce/orders_raw.csv(原始髒資料) + - datasets/ecommerce/customers.csv + - datasets/ecommerce/products.csv +""" +import pandas as pd +import plotly.express as px +import plotly.graph_objects as go +from plotly.subplots import make_subplots + + +# ============================================================ +# 🟢 送分題(每題 10 分,共 30 分) +# ============================================================ + +def green_plotly_bar(): + """ + 用 Plotly Express 畫出每個商品類別 (category) 的總營收長條圖 + 資料來源:orders_enriched.csv + 回傳 plotly Figure 物件 + 提示:px.bar() + """ + df = pd.read_csv('../datasets/ecommerce/orders_enriched.csv') + data = df.groupby('category')['amount'].sum().reset_index() + fig = px.bar(data, x='category', y='amount', title='Total Revenue by Category') + return fig + + +def green_plotly_line(): + """ + 用 Plotly Express 畫出月營收趨勢折線圖 + 資料來源:orders_enriched.csv + 回傳 plotly Figure 物件 + 提示:先 groupby 月份算總營收,再 px.line() + """ + df = pd.read_csv('../datasets/ecommerce/orders_enriched.csv') + df['order_date'] = pd.to_datetime(df['order_date']) + df['month'] = df['order_date'].dt.to_period('M').astype(str) + data = df.groupby('month')['amount'].sum().reset_index() + fig = px.line(data, x='month', y='amount', title='Monthly Revenue Trend') + return fig + + +def green_plotly_pie(): + """ + 用 Plotly Express 畫出 VIP 等級 (vip_level) 的訂單數佔比圓餅圖 + 資料來源:orders_enriched.csv + 回傳 plotly Figure 物件 + 提示:px.pie() + """ + df = pd.read_csv('../datasets/ecommerce/orders_enriched.csv') + fig = px.pie(df, names='vip_level', title='Order Distribution by VIP Level') + return fig + + +# ============================================================ +# 🟡 核心題(每題 15 分,共 45 分) +# ============================================================ + +def yellow_clean_and_merge(raw_path, customers_path, products_path): + """ + 完整 ETL:從髒資料到合併完成的 DataFrame + 1. 讀取 orders_raw.csv 並清理(欄位名稱、金額、日期、缺值、去重) + 2. 合併 customers.csv 和 products.csv + 回傳:合併後的 DataFrame + """ + df = pd.read_csv(raw_path) + df.columns = df.columns.str.strip().str.lower() + df['amount'] = (df['amount'] + .astype(str) + .str.replace('$', '', regex=False) + .str.replace(',', '', regex=False) + .astype(float)) + df['order_date'] = pd.to_datetime(df['order_date'], errors='coerce') + df = df.dropna(subset=['amount', 'order_date']).drop_duplicates() + customers = pd.read_csv(customers_path) + products = pd.read_csv(products_path) + df = pd.merge(df, customers, on='customer_id', how='left') + df = pd.merge(df, products, on='product_id', how='left') + + return df + + +def yellow_kpi_summary(df): + """ + 計算 4 個核心 KPI,回傳 dict: + { + "total_revenue": float, # 總營收 + "order_count": int, # 訂單數 + "active_customers": int, # 不重複客戶數 + "avg_order_value": float, # 平均客單價 + } + """ + summary = { + "total_revenue": float(df['amount'].sum()), + "order_count": int(len(df)), + "active_customers": int(df['customer_id'].nunique()), + "avg_order_value": float(df['amount'].mean()) + } + return summary + +def yellow_plotly_scatter(df): + """ + 用 Plotly Express 畫互動散佈圖: + - X:商品單價 (unit_price) + - Y:訂單金額 (amount) + - 顏色:商品類別 (category) + - hover 顯示:商品名稱 (product_name) + 回傳 plotly Figure 物件 + 提示:px.scatter(hover_data=['product_name']) + """ + fig = px.scatter( + df, + x='unit_price', + y='amount', + color='category', + hover_data=['product_name'], + title='Unit Price vs Order Amount' + ) + return fig + + +# ============================================================ +# 🔴 挑戰題(25 分) +# ============================================================ + +def red_dashboard(): + """ + Capstone:完整的互動式儀表板 + + 流程: + 1. 清理 orders_raw.csv + 合併三張表 + 2. 建立 2×2 subplot dashboard(用 plotly make_subplots): + - 左上:月營收趨勢 (line) + - 右上:Top 10 商品營收 (bar) + - 左下:各地區營收 (bar) + - 右下:類別營收佔比 (pie/donut) + 3. 設定整體標題 + + 回傳 plotly Figure 物件 + 提示:from plotly.subplots import make_subplots + """ +