diff --git a/homework/m1_numpy.py b/homework/m1_numpy.py
index 5ff41ab..dfb6196 100644
--- a/homework/m1_numpy.py
+++ b/homework/m1_numpy.py
@@ -19,19 +19,25 @@
def green_mean():
"""建立 [10, 20, 30, 40, 50],回傳所有元素的平均值 (float)"""
# TODO: 你的程式碼
- pass
+ arr = np.array([10, 20, 30, 40, 50])
+
+ return arr.mean()
def green_double():
"""建立 [10, 20, 30, 40, 50],回傳所有元素乘以 2 的 ndarray"""
# TODO: 你的程式碼
- pass
+ arr = np.array([10, 20, 30, 40, 50])
+
+ return arr * 2
def green_filter():
"""建立 [10, 20, 30, 40, 50],回傳大於 25 的元素 (ndarray)"""
# TODO: 你的程式碼
- pass
+ arr = np.array([10, 20, 30, 40, 50])
+
+ return arr[arr > 25]
# ============================================================
@@ -42,7 +48,7 @@ def green_filter():
def yellow_expensive_count(prices):
"""回傳單價 > 1000 的商品數量 (int)"""
# TODO: 你的程式碼
- pass
+ return (prices > 1000).sum()
def yellow_top3_stock_indices(stocks):
@@ -51,7 +57,7 @@ def yellow_top3_stock_indices(stocks):
提示:np.argsort
"""
# TODO: 你的程式碼
- pass
+ return np.argsort(-stocks)[0:3]
def yellow_restock_cost(prices, stocks):
@@ -60,7 +66,9 @@ def yellow_restock_cost(prices, stocks):
提示:布林遮罩 + .sum()
"""
# TODO: 你的程式碼
- pass
+ product = prices[prices < 500]
+
+ return (product * 50).sum()
# ============================================================
@@ -77,4 +85,8 @@ def red_double11_prices(prices, stocks):
提示:np.where 可以巢狀使用
"""
# TODO: 你的程式碼
- pass
+ return np.where(
+ stocks >= 100,
+ prices * 0.7,
+ np.where(stocks >= 20, prices * 0.9, prices)
+ )
\ No newline at end of file
diff --git a/homework/m2_pandas_cleaning.py b/homework/m2_pandas_cleaning.py
index fa36bff..04e9ef8 100644
--- a/homework/m2_pandas_cleaning.py
+++ b/homework/m2_pandas_cleaning.py
@@ -20,7 +20,7 @@ def green_read_csv():
提示:pd.read_csv()
"""
# TODO: 你的程式碼
- pass
+ return pd.read_csv("datasets/ecommerce/orders_raw.csv")
def green_shape(df):
@@ -29,7 +29,7 @@ def green_shape(df):
提示:df.shape
"""
# TODO: 你的程式碼
- pass
+ return df.shape
def green_dtypes(df):
@@ -38,7 +38,7 @@ def green_dtypes(df):
提示:df.dtypes
"""
# TODO: 你的程式碼
- pass
+ return df.dtypes
# ============================================================
@@ -52,7 +52,10 @@ def yellow_clean_columns(df):
提示:df.columns.str.strip().str.lower()
"""
# TODO: 你的程式碼
- pass
+ clean = df.copy()
+ clean.columns = clean.columns.str.strip().str.lower()
+
+ return clean
def yellow_clean_amount(df):
@@ -63,7 +66,10 @@ def yellow_clean_amount(df):
提示:.str.replace() + .astype(float)
"""
# TODO: 你的程式碼
- pass
+ clean = df.copy()
+ clean["amount"] = clean["amount"].str.replace("$", "", regex=False).str.replace(",", "", regex=False).astype(float)
+
+ return clean
def yellow_drop_duplicates(df):
@@ -72,7 +78,7 @@ def yellow_drop_duplicates(df):
提示:df.drop_duplicates()
"""
# TODO: 你的程式碼
- pass
+ return df.drop_duplicates()
# ============================================================
@@ -93,4 +99,11 @@ def red_clean_orders(path):
提示:pd.to_datetime(errors='coerce')
"""
# TODO: 你的程式碼
- pass
+ df = pd.read_csv(path)
+ df.columns = df.columns.str.strip().str.lower()
+ df["amount"] = df["amount"].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"])
+ df = df.drop_duplicates()
+
+ return df
\ No newline at end of file
diff --git a/homework/m3_pandas_advanced.py b/homework/m3_pandas_advanced.py
index 68410e8..afa4d8f 100644
--- a/homework/m3_pandas_advanced.py
+++ b/homework/m3_pandas_advanced.py
@@ -24,19 +24,26 @@ def green_load_and_merge():
提示:pd.merge(how='left')
"""
# TODO: 你的程式碼
- pass
+ 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 = orders.merge(customers, on="customer_id", how="left")
+ df = df.merge(products, on="product_id", how="left")
+
+ return df
def green_row_count(df):
"""回傳 DataFrame 的列數 (int)"""
# TODO: 你的程式碼
- pass
+ return len(df)
def green_column_list(df):
"""回傳 DataFrame 的所有欄位名稱 (list)"""
# TODO: 你的程式碼
- pass
+ return list(df.columns)
# ============================================================
@@ -50,7 +57,9 @@ def yellow_top_category(df):
提示:groupby('category')['amount'].sum()
"""
# TODO: 你的程式碼
- pass
+ revenue = df.groupby("category")["amount"].sum().idxmax()
+
+ return revenue
def yellow_gold_vip_stats(df):
@@ -60,7 +69,10 @@ def yellow_gold_vip_stats(df):
提示:df[df['vip_level'] == 'Gold']
"""
# TODO: 你的程式碼
- pass
+ orders = df[df["vip_level"] == "Gold"]["order_id"].count()
+ total = df[df["vip_level"] == "Gold"]["amount"].sum()
+
+ return (orders, total)
def yellow_region_avg_amount(df):
@@ -70,7 +82,9 @@ def yellow_region_avg_amount(df):
提示:groupby('region')['amount'].mean()
"""
# TODO: 你的程式碼
- pass
+ average = df.groupby("region")["amount"].mean()
+
+ return average
# ============================================================
@@ -94,4 +108,13 @@ def red_rfm_top5(df):
提示:groupby('customer_id').agg(...)
"""
# TODO: 你的程式碼
- pass
+ rfm = df.groupby("customer_id").agg(
+ customer_name=("customer_name", "first"),
+ R=("order_date", "max"),
+ F=("order_id", "count"),
+ M=("amount", "sum")
+ ).reset_index()
+
+ top5 = rfm.sort_values("M", ascending=False).head(5)[["customer_id", "customer_name", "R", "F", "M"]]
+
+ return top5
\ No newline at end of file
diff --git a/homework/m4_timeseries.py b/homework/m4_timeseries.py
index 047af6a..496e209 100644
--- a/homework/m4_timeseries.py
+++ b/homework/m4_timeseries.py
@@ -27,7 +27,10 @@ def green_avg_by_month():
提示:df['order_date'].dt.month
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+ month = df["order_date"].dt.month
+
+ return df.groupby(month)["amount"].mean().round(1)
def green_top3_dates():
@@ -37,7 +40,10 @@ def green_top3_dates():
提示:value_counts().head(3)
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+ top3 = df["order_date"].dt.date.value_counts().head(3)
+
+ return top3
def green_date_range():
@@ -46,7 +52,11 @@ def green_date_range():
格式為 pandas Timestamp
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+ earliest = df["order_date"].min()
+ latest = df["order_date"].max()
+
+ return (earliest, latest)
# ============================================================
@@ -60,7 +70,10 @@ def yellow_monthly_revenue():
提示:set_index('order_date').resample('ME')['amount'].sum()
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+ monthly = df.set_index("order_date").resample("ME")["amount"].sum()
+
+ return monthly
def yellow_rolling_avg(monthly_revenue):
@@ -71,7 +84,7 @@ def yellow_rolling_avg(monthly_revenue):
提示:.rolling(window=3).mean()
"""
# TODO: 你的程式碼
- pass
+ return monthly_revenue.rolling(window=3).mean()
def yellow_category_median(df):
@@ -81,7 +94,7 @@ def yellow_category_median(df):
提示:groupby + median + sort_values
"""
# TODO: 你的程式碼
- pass
+ return df.groupby("category")["amount"].median().sort_values(ascending=False)
# ============================================================
@@ -101,4 +114,15 @@ def red_monthly_report():
提示:resample + agg + pct_change
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+
+ monthly = df.set_index("order_date").resample("ME").agg(
+ order_count=("order_id", "count"),
+ revenue=("amount", "sum"),
+ active_customers=("customer_id", "nunique")
+ )
+
+ monthly["avg_order_value"] = monthly["revenue"] / monthly["order_count"]
+ monthly["revenue_growth"] = monthly["revenue"].pct_change()
+
+ return monthly
\ No newline at end of file
diff --git a/homework/m5_visualization.py b/homework/m5_visualization.py
index 7e7335d..f7fa554 100644
--- a/homework/m5_visualization.py
+++ b/homework/m5_visualization.py
@@ -29,7 +29,19 @@ def green_bar_category():
提示:sns.countplot 或 value_counts().plot.bar()
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+
+ fig, ax = plt.subplots(figsize=(8, 4))
+
+ sns.countplot(data=df, x="category", ax=ax)
+
+ ax.set_title("Order Count by Category")
+ ax.set_xlabel("Category")
+ ax.set_ylabel("Order Count")
+
+ fig.tight_layout()
+
+ return fig
def green_hist_amount():
@@ -39,7 +51,19 @@ def green_hist_amount():
提示:sns.histplot(bins=20) 或 plt.hist()
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+
+ fig, ax = plt.subplots(figsize=(8, 4))
+
+ sns.histplot(data=df, x="amount", bins=20, ax=ax)
+
+ ax.set_title("Distribution of Order Amount")
+ ax.set_xlabel("Amount")
+ ax.set_ylabel("Count")
+
+ fig.tight_layout()
+
+ return fig
def green_set_labels():
@@ -51,7 +75,23 @@ def green_set_labels():
回傳 matplotlib Figure 物件
"""
# TODO: 你的程式碼
- pass
+ fig, ax = plt.subplots()
+
+ brands = ["Samsung", "Apple", "Xiaomi", "OPPO", "vivo", "Others"]
+ market_share = [22, 20, 11, 10, 7, 29]
+
+ ax.bar(brands, market_share)
+ ax.set_title("Global Smartphones Brand Market Share")
+ ax.set_xlabel("Brand")
+ ax.set_ylabel("Market Share (%)")
+ ax.set_ylim(0, 35)
+
+ for i, value in enumerate(market_share):
+ ax.text(i, value + 0.5, str(value) + "%", ha="center")
+
+ fig.tight_layout()
+
+ return fig
# ============================================================
@@ -68,7 +108,26 @@ def yellow_line_region_trend():
提示:分別 groupby 再 plot,或用 sns.lineplot(hue='region')
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+
+ df = df[df["region"].isin(["North", "South"])].copy()
+ df["month"] = df["order_date"].dt.to_period("M").dt.to_timestamp()
+
+ monthly = df.groupby(["month", "region"])["amount"].sum().reset_index()
+
+ fig, ax = plt.subplots(figsize=(10, 5))
+
+ sns.lineplot(data=monthly, x="month", y="amount", hue="region", marker="o", ax=ax)
+
+ ax.set_title("Monthly Revenue Trend: North vs South")
+ ax.set_xlabel("Month")
+ ax.set_ylabel("Total Revenue")
+ ax.legend(title="Region")
+
+ fig.autofmt_xdate()
+ fig.tight_layout()
+
+ return fig
def yellow_box_vip():
@@ -78,7 +137,19 @@ def yellow_box_vip():
提示:sns.boxplot(x='vip_level', y='amount', data=df)
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+
+ fig, ax = plt.subplots(figsize=(8, 4))
+
+ sns.boxplot(data=df, x="vip_level", y="amount", ax=ax)
+
+ ax.set_title("Order Amount Distribution by VIP Level")
+ ax.set_xlabel("VIP Level")
+ ax.set_ylabel("Order Amount")
+
+ fig.tight_layout()
+
+ return fig
def yellow_scatter_price_amount():
@@ -88,7 +159,19 @@ def yellow_scatter_price_amount():
提示:plt.scatter() 或 sns.scatterplot()
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+
+ fig, ax = plt.subplots(figsize=(8, 4))
+
+ sns.scatterplot(data=df, x="unit_price", y="amount", ax=ax)
+
+ ax.set_title("Unit Price vs Order Amount")
+ ax.set_xlabel("Unit Price")
+ ax.set_ylabel("Order Amount")
+
+ fig.tight_layout()
+
+ return fig
# ============================================================
@@ -107,4 +190,49 @@ def red_category_dashboard(category="Electronics"):
提示:fig, axes = plt.subplots(2, 2, figsize=(14, 10))
"""
# TODO: 你的程式碼
- pass
+ df = _load_data()
+
+ sub = df[df["category"] == category].copy()
+ sub["month"] = sub["order_date"].dt.to_period("M").dt.to_timestamp()
+
+ fig, ax = plt.subplots(2, 2, figsize=(14, 10))
+
+ # 1. 左上:月營收趨勢 (折線圖)
+ monthly = sub.groupby("month")["amount"].sum().reset_index()
+
+ sns.lineplot(data=monthly, x="month", y="amount", ax=ax[0, 0])
+
+ ax[0, 0].set_title(f"{category} Monthly Revenue Trend")
+ ax[0, 0].set_xlabel("Month")
+ ax[0, 0].set_ylabel("Revenue")
+
+ # 2. 右上:各地區營收 (長條圖)
+ region_sales = sub.groupby("region")["amount"].sum().reset_index()
+
+ sns.barplot(data=region_sales, x="region", y="amount", ax=ax[0, 1])
+
+ ax[0, 1].set_title(f"{category} Revenue by Region")
+ ax[0, 1].set_xlabel("Region")
+ ax[0, 1].set_ylabel("Revenue")
+
+ # 3. 左下:Top 5 商品營收 (水平長條圖)
+ top_products = sub.groupby("product_name")["amount"].sum().sort_values(ascending=False).head(5).reset_index()
+
+ sns.barplot(data=top_products, x="amount", y="product_name", ax=ax[1, 0])
+
+ ax[1, 0].set_title(f"{category} Top 5 Products by Revenue")
+ ax[1, 0].set_xlabel("Revenue")
+ ax[1, 0].set_ylabel("Product Name")
+
+ # 4. 右下:訂單金額分佈 (直方圖)
+ sns.histplot(data=sub, x="amount", bins=20, ax=ax[1, 1])
+
+ ax[1, 1].set_title(f"{category} Order Amount Distribution")
+ ax[1, 1].set_xlabel("Order Amount")
+ ax[1, 1].set_ylabel("Count")
+
+ fig.suptitle(f"Category Dashboard: {category}", fontsize=16)
+ fig.autofmt_xdate()
+ fig.tight_layout()
+
+ return fig
\ No newline at end of file
diff --git a/homework/m6_plotly_capstone.py b/homework/m6_plotly_capstone.py
index 0e2c32a..c739686 100644
--- a/homework/m6_plotly_capstone.py
+++ b/homework/m6_plotly_capstone.py
@@ -26,7 +26,31 @@ def green_plotly_bar():
提示:px.bar()
"""
# TODO: 你的程式碼
- pass
+ df = pd.read_csv("datasets/ecommerce/orders_enriched.csv")
+
+ category_revenue = df.groupby("category")["amount"].sum().reset_index().sort_values("amount", ascending=False)
+
+ fig = px.bar(
+ category_revenue,
+ x="category",
+ y="amount",
+ title="Revenue by Category",
+ labels={"category": "Category", "amount": "Total Revenue"},
+ text="amount"
+ )
+
+ fig.update_traces(
+ texttemplate="%{text:.0f}",
+ textposition="outside"
+ )
+ fig.update_layout(
+ xaxis_title="Category",
+ yaxis_title="Total Revenue",
+ uniformtext_minsize=8,
+ uniformtext_mode="hide"
+ )
+
+ return fig
def green_plotly_line():
@@ -37,7 +61,27 @@ def green_plotly_line():
提示:先 groupby 月份算總營收,再 px.line()
"""
# TODO: 你的程式碼
- pass
+ 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").dt.to_timestamp()
+
+ monthly = df.groupby("month")["amount"].sum().reset_index().sort_values("month")
+
+ fig = px.line(
+ monthly,
+ x="month",
+ y="amount",
+ markers=True,
+ title="Monthly Revenue Trend",
+ labels={"month": "Month", "amount": "Total Revenue"}
+ )
+
+ fig.update_layout(
+ xaxis_title="Month",
+ yaxis_title="Total Revenue"
+ )
+
+ return fig
def green_plotly_pie():
@@ -48,7 +92,20 @@ def green_plotly_pie():
提示:px.pie()
"""
# TODO: 你的程式碼
- pass
+ df = pd.read_csv("datasets/ecommerce/orders_enriched.csv")
+
+ vip_counts = df["vip_level"].value_counts().reset_index()
+ vip_counts.columns = ["vip_level", "order_count"]
+
+ fig = px.pie(vip_counts, names="vip_level", values="order_count", title="Order Count Share by VIP Level")
+
+ fig.update_traces(
+ textinfo="label+percent",
+ hovertemplate="VIP Level: %{label}
Order Count: %{value}
Share: %{percent}"
+ )
+ fig.update_layout(title_x=0.5)
+
+ return fig
# ============================================================
@@ -63,7 +120,27 @@ def yellow_clean_and_merge(raw_path, customers_path, products_path):
回傳:合併後的 DataFrame
"""
# TODO: 你的程式碼
- pass
+ orders = pd.read_csv(raw_path)
+ customers = pd.read_csv(customers_path)
+ products = pd.read_csv(products_path)
+
+ # 欄位名稱
+ orders.columns = orders.columns.str.strip().str.lower()
+
+ # 金額
+ orders["amount"] = (orders["amount"].astype(str).str.replace("$", "", regex=False).str.replace(",", "", regex=False).astype(float))
+
+ # 日期
+ orders["order_date"] = pd.to_datetime(orders["order_date"], errors="coerce")
+
+ # 缺值、去重
+ orders = orders.dropna(subset=["amount", "order_date"]).drop_duplicates()
+
+ # 合併
+ merge = orders.merge(customers, on="customer_id", how="left")
+ merge = merge.merge(products, on="product_id", how="left")
+
+ return merge
def yellow_kpi_summary(df):
@@ -77,7 +154,17 @@ def yellow_kpi_summary(df):
}
"""
# TODO: 你的程式碼
- pass
+ total_revenue = df["amount"].sum()
+ order_count = len(df)
+ active_customers = df["customer_id"].nunique()
+ avg_order_value = total_revenue / order_count
+
+ return {
+ "total_revenue": float(total_revenue),
+ "order_count": int(order_count),
+ "active_customers": int(active_customers),
+ "avg_order_value": float(avg_order_value),
+ }
def yellow_plotly_scatter(df):
@@ -91,7 +178,27 @@ def yellow_plotly_scatter(df):
提示:px.scatter(hover_data=['product_name'])
"""
# TODO: 你的程式碼
- pass
+ fig = px.scatter(
+ df,
+ x="unit_price",
+ y="amount",
+ color="category",
+ hover_data=["product_name"],
+ title="Unit Price vs Order Amount by Category",
+ labels={
+ "unit_price": "Unit Price",
+ "amount": "Order Amount",
+ "category": "Category",
+ "product_name": "Product Name"
+ }
+ )
+
+ fig.update_layout(
+ xaxis_title="Unit Price",
+ yaxis_title="Order Amount"
+ )
+
+ return fig
# ============================================================
@@ -115,4 +222,38 @@ def red_dashboard():
提示:from plotly.subplots import make_subplots
"""
# TODO: 你的程式碼
- pass
+ df = yellow_clean_and_merge(
+ "datasets/ecommerce/orders_raw.csv",
+ "datasets/ecommerce/customers.csv",
+ "datasets/ecommerce/products.csv",
+ )
+
+ df["month"] = df["order_date"].dt.to_period("M").dt.to_timestamp()
+
+ monthly = df.groupby("month")["amount"].sum().reset_index().sort_values("month")
+ top_products = df.groupby("product_name")["amount"].sum().sort_values(ascending=False).head(10).reset_index()
+ region_revenue = df.groupby("region")["amount"].sum().reset_index().sort_values("amount", ascending=False)
+ category_revenue = df.groupby("category")["amount"].sum().reset_index().sort_values("amount", ascending=False)
+
+ fig = make_subplots(
+ rows=2,
+ cols=2,
+ subplot_titles=["Monthly Revenue Trend", "Top 10 Products by Revenue", "Revenue by Region", "Revenue Share by Category"],
+ specs=[[{"type": "xy"}, {"type": "xy"}], [{"type": "xy"}, {"type": "domain"}]]
+ )
+
+ fig.add_trace(go.Scatter(x=monthly["month"], y=monthly["amount"], mode="lines+markers", name="Monthly Revenue"), row=1, col=1)
+ fig.add_trace(go.Bar(x=top_products["product_name"], y=top_products["amount"], name="Top Products"), row=1, col=2)
+ fig.add_trace(go.Bar(x=region_revenue["region"], y=region_revenue["amount"], name="Region Revenue"), row=2, col=1)
+ fig.add_trace(go.Pie(labels=category_revenue["category"], values=category_revenue["amount"], hole=0.4, name="Category Revenue"), row=2, col=2)
+
+ fig.update_xaxes(title_text="Month", row=1, col=1)
+ fig.update_yaxes(title_text="Revenue", row=1, col=1)
+ fig.update_xaxes(title_text="Product Name", row=1, col=2)
+ fig.update_yaxes(title_text="Revenue", row=1, col=2)
+ fig.update_xaxes(title_text="Region", row=2, col=1)
+ fig.update_yaxes(title_text="Revenue", row=2, col=1)
+
+ fig.update_layout(title_text="Ecommerce Dashboard", title_x=0.5, height=800, showlegend=True)
+
+ return fig
\ No newline at end of file