From 05f64d59ea92df227b00052f0e9551d69fa71c58 Mon Sep 17 00:00:00 2001 From: bellayang312-source Date: Mon, 8 Jun 2026 19:48:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E6=88=90=E4=BD=9C=E6=A5=AD=E7=B9=B3?= =?UTF-8?q?=E4=BA=A4=20-=E6=A5=8A=E8=88=92=E5=AA=81-=20AIPE03?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- homework/m1_numpy.py | 15 ++++ homework/m2_pandas_cleaning.py | 18 ++++ homework/m3_pandas_advanced.py | 25 ++++++ homework/m4_timeseries.py | 35 ++++++++ homework/m5_visualization.py | 55 ++++++++++++ homework/m6_plotly_capstone.py | 151 +++++++++++++++++++++++++++++++++ 6 files changed, 299 insertions(+) diff --git a/homework/m1_numpy.py b/homework/m1_numpy.py index 5ff41ab..eb75e1c 100644 --- a/homework/m1_numpy.py +++ b/homework/m1_numpy.py @@ -19,18 +19,24 @@ def green_mean(): """建立 [10, 20, 30, 40, 50],回傳所有元素的平均值 (float)""" # TODO: 你的程式碼 + float = np.array([10, 20, 30, 40, 50]) + return np.mean(float) pass def green_double(): """建立 [10, 20, 30, 40, 50],回傳所有元素乘以 2 的 ndarray""" # TODO: 你的程式碼 + ndarray = np.array([10, 20, 30, 40, 50]) + return ndarray * 2 pass def green_filter(): """建立 [10, 20, 30, 40, 50],回傳大於 25 的元素 (ndarray)""" # TODO: 你的程式碼 + ndarray = np.array([10, 20, 30, 40, 50]) + return ndarray[ndarray >25] pass @@ -42,6 +48,7 @@ def green_filter(): def yellow_expensive_count(prices): """回傳單價 > 1000 的商品數量 (int)""" # TODO: 你的程式碼 + return (prices > 1000).sum() pass @@ -51,6 +58,7 @@ def yellow_top3_stock_indices(stocks): 提示:np.argsort """ # TODO: 你的程式碼 + return np.argsort(stocks)[::-1][:3] pass @@ -60,6 +68,7 @@ def yellow_restock_cost(prices, stocks): 提示:布林遮罩 + .sum() """ # TODO: 你的程式碼 + return (prices[prices < 500] * 50).sum() pass @@ -77,4 +86,10 @@ def red_double11_prices(prices, stocks): 提示:np.where 可以巢狀使用 """ # TODO: 你的程式碼 + return np.where( + stocks >= 100, prices * 0.7, + np.where( + (stocks >=20) & (stocks <100), prices *0.9, + prices + )) pass diff --git a/homework/m2_pandas_cleaning.py b/homework/m2_pandas_cleaning.py index fa36bff..dc3e999 100644 --- a/homework/m2_pandas_cleaning.py +++ b/homework/m2_pandas_cleaning.py @@ -20,6 +20,7 @@ def green_read_csv(): 提示:pd.read_csv() """ # TODO: 你的程式碼 + return pd.read_csv("../datasets/ecommerce/orders_raw.csv") pass @@ -29,6 +30,8 @@ def green_shape(df): 提示:df.shape """ # TODO: 你的程式碼 + + return df.shape pass @@ -38,6 +41,7 @@ def green_dtypes(df): 提示:df.dtypes """ # TODO: 你的程式碼 + return df.dtypes pass @@ -52,6 +56,9 @@ def yellow_clean_columns(df): 提示:df.columns.str.strip().str.lower() """ # TODO: 你的程式碼 + df_copy = df.copy() + df_copy.columns = df_copy.columns.str.strip().str.lower() + return df_copy pass @@ -63,6 +70,9 @@ def yellow_clean_amount(df): 提示:.str.replace() + .astype(float) """ # TODO: 你的程式碼 + df_copy = df.copy() + df_copy["amount"] = df_copy["amount"].str.replace("$","",regex=False).str.replace(",","",regex=False).astype(float) + return df_copy pass @@ -72,6 +82,7 @@ def yellow_drop_duplicates(df): 提示:df.drop_duplicates() """ # TODO: 你的程式碼 + return df.drop_duplicates() pass @@ -93,4 +104,11 @@ def red_clean_orders(path): 提示:pd.to_datetime(errors='coerce') """ # TODO: 你的程式碼 + 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 pass diff --git a/homework/m3_pandas_advanced.py b/homework/m3_pandas_advanced.py index 68410e8..3e28714 100644 --- a/homework/m3_pandas_advanced.py +++ b/homework/m3_pandas_advanced.py @@ -24,18 +24,27 @@ def green_load_and_merge(): 提示:pd.merge(how='left') """ # TODO: 你的程式碼 + df1 = pd.read_csv("../datasets/ecommerce/orders_clean.csv") + df2 = pd.read_csv("../datasets/ecommerce/customers.csv") + df3 = pd.read_csv("../datasets/ecommerce/products.csv") + df = df1.merge(df2, on = "customer_id", how = "left") + df = df.merge(df3, on = "product_id", how = "left") + return df + pass def green_row_count(df): """回傳 DataFrame 的列數 (int)""" # TODO: 你的程式碼 + return df.shape[0] pass def green_column_list(df): """回傳 DataFrame 的所有欄位名稱 (list)""" # TODO: 你的程式碼 + return list(df.columns) pass @@ -50,6 +59,7 @@ def yellow_top_category(df): 提示:groupby('category')['amount'].sum() """ # TODO: 你的程式碼 + return df.groupby("category")["amount"].sum().sort_values().idxmax() pass @@ -60,6 +70,9 @@ def yellow_gold_vip_stats(df): 提示:df[df['vip_level'] == 'Gold'] """ # TODO: 你的程式碼 + order_count = df[df["vip_level"] == "Gold"].shape[0] + total_amount = df[df["vip_level"] == "Gold"]["amount"].sum() + return (order_count, total_amount) pass @@ -70,6 +83,7 @@ def yellow_region_avg_amount(df): 提示:groupby('region')['amount'].mean() """ # TODO: 你的程式碼 + return df.groupby("region")["amount"].mean() pass @@ -94,4 +108,15 @@ def red_rfm_top5(df): 提示:groupby('customer_id').agg(...) """ # TODO: 你的程式碼 + result_df = df.groupby("customer_id").agg({ + "customer_name" : "first", + "order_date":"max", + "order_id" : "count", + "amount" : "sum" +}).rename(columns={ + "order_date" : "R", + "order_id" : "F", + "amount" : "M" +}).sort_values("M", ascending= False).head().reset_index() + return result_df pass diff --git a/homework/m4_timeseries.py b/homework/m4_timeseries.py index 047af6a..4abe161 100644 --- a/homework/m4_timeseries.py +++ b/homework/m4_timeseries.py @@ -27,6 +27,8 @@ def green_avg_by_month(): 提示:df['order_date'].dt.month """ # TODO: 你的程式碼 + df = _load_data() + return df.groupby(df['order_date'].dt.month)["amount"].mean() pass @@ -37,6 +39,8 @@ def green_top3_dates(): 提示:value_counts().head(3) """ # TODO: 你的程式碼 + df = _load_data() + return df["order_date"].value_counts().head(3) pass @@ -46,6 +50,10 @@ def green_date_range(): 格式為 pandas Timestamp """ # TODO: 你的程式碼 + df = _load_data() + max_date = df["order_date"].max() + min_date = df["order_date"].min() + return (min_date, max_date) pass @@ -60,6 +68,8 @@ def yellow_monthly_revenue(): 提示:set_index('order_date').resample('ME')['amount'].sum() """ # TODO: 你的程式碼 + df = _load_data() + return df.set_index(["order_date"]).resample("ME")["amount"].sum() pass @@ -71,6 +81,7 @@ def yellow_rolling_avg(monthly_revenue): 提示:.rolling(window=3).mean() """ # TODO: 你的程式碼 + return monthly_revenue.rolling(window=3).mean() pass @@ -81,6 +92,7 @@ def yellow_category_median(df): 提示:groupby + median + sort_values """ # TODO: 你的程式碼 + return df.groupby(df["category"])["amount"].median().sort_values(ascending=False) pass @@ -101,4 +113,27 @@ def red_monthly_report(): 提示:resample + agg + pct_change """ # TODO: 你的程式碼 + df = _load_data() + monthly_report = ( + df + .set_index('order_date') + .resample("ME") + .agg({ + "order_id" : "count", + "amount" : "sum", + "customer_id" : "nunique" +}).rename(columns={ + "order_id" : "order_count", + "amount" : "revenue", + "customer_id" : "active_customers" +}) +) + + monthly_report.index = monthly_report.index.to_period("M") + + monthly_report["avg_order_value"] = (monthly_report["revenue"]/monthly_report["order_count"]) + + monthly_report["revenue_growth"] = (monthly_report["revenue"].pct_change() * 100 ) + + return monthly_report pass diff --git a/homework/m5_visualization.py b/homework/m5_visualization.py index 7e7335d..eeda241 100644 --- a/homework/m5_visualization.py +++ b/homework/m5_visualization.py @@ -29,6 +29,10 @@ def green_bar_category(): 提示:sns.countplot 或 value_counts().plot.bar() """ # TODO: 你的程式碼 + df = _load_data() + plt.figure(figsize = (8, 4)) + sns.countplot(data = df, x = "category") + return plt.gcf() pass @@ -39,6 +43,10 @@ def green_hist_amount(): 提示:sns.histplot(bins=20) 或 plt.hist() """ # TODO: 你的程式碼 + df = _load_data() + plt.figure(figsize=(8,4)) + sns.histplot(bins = 20, x = "amount", data = df) + return plt.gcf() pass @@ -51,6 +59,13 @@ def green_set_labels(): 回傳 matplotlib Figure 物件 """ # TODO: 你的程式碼 + df = _load_data() + plt.figure(figsize = (8, 4)) + sns.countplot(data = df, x = "category") + plt.title("category_order_count") + plt.xlabel("category") + plt.ylabel("order_count") + return plt.gcf() pass @@ -68,6 +83,21 @@ def yellow_line_region_trend(): 提示:分別 groupby 再 plot,或用 sns.lineplot(hue='region') """ # TODO: 你的程式碼 + df = _load_data() + df["month"] = df["order_date"].dt.to_period("M") + region_monthly_revenue = ( + df[df["region"].isin(["North", "South"])] + .groupby(["month", "region"])["amount"] + .sum() + .unstack() + ) + fig, ax = plt.subplots(figsize=(8, 4)) + + region_monthly_revenue.plot( + marker="o", + ax=ax + ) + return fig pass @@ -78,6 +108,10 @@ def yellow_box_vip(): 提示:sns.boxplot(x='vip_level', y='amount', data=df) """ # TODO: 你的程式碼 + df = _load_data() + fig, ax = plt.subplots(figsize=(8, 4)) + sns.boxplot(x = "vip_level", y = "amount", data = df) + return fig pass @@ -88,6 +122,10 @@ def yellow_scatter_price_amount(): 提示:plt.scatter() 或 sns.scatterplot() """ # TODO: 你的程式碼 + df = _load_data() + plt.figure(figsize=(8, 4)) + plt.scatter(x = "unit_price", y = "amount", data = df) + return plt.gcf() pass @@ -107,4 +145,21 @@ def red_category_dashboard(category="Electronics"): 提示:fig, axes = plt.subplots(2, 2, figsize=(14, 10)) """ # TODO: 你的程式碼 + df = _load_data() + df["month"] = df["order_date"].dt.to_period("M").astype("str") + df = df[df["category"].isin(["Electronics"])] + fig, axes = plt.subplots(2, 2, figsize = (14,10)) + + + sns.lineplot(data=df, x = "month", y = "amount", marker="o", ax = axes[0, 0], estimator = "sum", errorbar=None ) + axes[0, 0].tick_params(axis='x', rotation=45) + + sns.barplot(data=df, x = "region", y = "amount", estimator="sum", + errorbar=None, ax = axes[0,1] ) + + df.groupby("product_name")["amount"].sum().sort_values(ascending=False).head(5).plot.barh(ax=axes[1,0]) + + sns.histplot(data = df, x = "amount", bins=20, ax=axes[1,1]) + + return fig pass diff --git a/homework/m6_plotly_capstone.py b/homework/m6_plotly_capstone.py index 0e2c32a..532c497 100644 --- a/homework/m6_plotly_capstone.py +++ b/homework/m6_plotly_capstone.py @@ -26,6 +26,17 @@ def green_plotly_bar(): 提示:px.bar() """ # TODO: 你的程式碼 + df = pd.read_csv("../datasets/ecommerce/orders_enriched.csv") + + category_revenue = df.groupby("category")["amount"].sum().reset_index().rename( + columns={"amount" : "revenue"} + ) + + fig = px.bar(category_revenue, + x = "category", + y = "revenue", + ) + return fig pass @@ -37,6 +48,12 @@ def green_plotly_line(): 提示:先 groupby 月份算總營收,再 px.line() """ # TODO: 你的程式碼 + 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) + monthly_revenue = df.groupby("month")["amount"].sum().reset_index().rename(columns={"amount" : "revenue"}) + fig = px.line(monthly_revenue, x = "month", y = "revenue", markers = "o") + return fig pass @@ -48,6 +65,12 @@ def green_plotly_pie(): 提示:px.pie() """ # TODO: 你的程式碼 + df = pd.read_csv("../datasets/ecommerce/orders_enriched.csv") + + vip_order_count = df["vip_level"].value_counts().reset_index().rename(columns={"count" : "order_count"}) + + fig = px.pie(vip_order_count, names="vip_level", values="order_count") + return fig pass @@ -63,6 +86,28 @@ def yellow_clean_and_merge(raw_path, customers_path, products_path): 回傳:合併後的 DataFrame """ # TODO: 你的程式碼 + 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) + ) + + orders["amount"] = pd.to_numeric(orders["amount"], errors="coerce") + orders["order_date"] = pd.to_datetime(orders["order_date"], errors="coerce") + + orders = orders.dropna(subset=["amount", "order_date", "qty"]) + orders = orders.drop_duplicates() + + new_df = pd.merge(orders, customers, on="customer_id", how="left") + new_df = pd.merge(new_df, products, on="product_id", how="left") + + return new_df pass @@ -77,6 +122,17 @@ def yellow_kpi_summary(df): } """ # TODO: 你的程式碼 + 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), + } pass @@ -91,6 +147,8 @@ def yellow_plotly_scatter(df): 提示:px.scatter(hover_data=['product_name']) """ # TODO: 你的程式碼 + fig = px.scatter(data_frame = df, x = "unit_price", y = "amount", color="category", hover_data=['product_name'] ) + return fig pass @@ -115,4 +173,97 @@ def red_dashboard(): 提示:from plotly.subplots import make_subplots """ # TODO: 你的程式碼 + orders = pd.read_csv("../datasets/ecommerce/orders_raw.csv") + customers = pd.read_csv("../datasets/ecommerce/customers.csv") + products = pd.read_csv("../datasets/ecommerce/products.csv") + orders.columns = orders.columns.str.strip().str.lower() + orders["amount"] = orders["amount"].str.replace("$","",regex=False).replace(",","",regex=False).astype(float, errors = "coerce") + orders["order_Date"] = pd.to_datetime(orders["order_Date"], errors = "coerce") + orders = orders.dropna(subset=["amount", "order_date","qty"]) + orders = orders.drop_duplicates() + df = pd.merge(orders, customers, on="customer_id", how="left") + df = pd.merge(df, products, on="product_id", how="left") + + df["month"] = df["order_date"].dt.to_period("M").astype(str) + + month_revenue = df.groupby("month")["amount"].sum().reset_index().rename(columns={"amount" :"revenue"}) + + top10_products = df.groupby("product_name")["amount"].sum().reset_index().rename(columns ={"amount" : "revenue"} ).sort_values("revenue",ascending=False).head(10) + + region_revenue = (df.groupby("region")["amount"] + .sum() + .reset_index() + .rename(columns ={"amount" : "revenue"}) + ) + + category_revenue = ( + df.groupby("category")["amount"] + .sum() + .reset_index() + .rename(columns={"amount": "revenue"}) +) + fig = make_subplots( + rows = 2, + cols = 2, + subplot_titles=[ + "月營收趨勢", + "Top 10 商品營收", + "各地區營收", + "類別營收佔比" + ], + specs = [ + [("type" : "xy"), {"type" : "xy"}], + [("type" : "xy"), {"type" : "domain"}], + ] + ) + + fig.add_trace( + go.Scatter( + x=month_revenue["month"], + y=month_revenue["revenue"], + mode="lines+markers", + name="月營收" + ), + row=1, + col=1 + ) + + fig.add_trace( + go.Bar( + x=top10_products["product_name"], + y=top10_products["revenue"], + name="Top 10 商品營收" + ), + row=1, + col=2 + ) + + fig.add_trace( + go.Bar( + x=region_revenue["region"], + y=region_revenue["revenue"], + name="各地區營收" + ), + row=2, + col=1 + ) + + fig.add_trace( + go.Pie( + labels=category_revenue["category"], + values=category_revenue["revenue"], + hole=0.4, + name="類別營收佔比" + ), + row=2, + col=2 + ) + + fig.update_layout( + title_text="E-commerce 互動式營運儀表板", + height=800, + showlegend=True + ) + + return fig pass