🎯 Mục tiêu bài học
Sau bài thực hành này, bạn sẽ:
✅ Thành thạo đọc, khám phá, lọc dữ liệu từ CSV
✅ Sử dụng Groupby + Aggregation để phân tích dữ liệu kinh doanh
✅ Merge nhiều bảng và tính toán trên kết quả
✅ Tạo báo cáo Pivot Table chuyên nghiệp
Thời gian: 2 giờ | Độ khó: Beginner → Hard | Yêu cầu: Hoàn thành Bài 6 (Pandas)
🟢 Phần 1: Tạo và Khám Phá DataFrame (Easy)
Ôn lại lý thuyết (Bài 6):
- DataFrame = bảng 2D, Series = 1 cột
- Tạo:
pd.DataFrame(dict),pd.read_csv() - Khám phá:
shape,head(),info(),describe() - Xử lý null:
isnull(),dropna(),fillna()
Bài 1.1: Tạo dataset bán hàng
1import pandas as pd2import numpy as np3np.random.seed(42)45n = 2006df = pd.DataFrame({7 "order_id": range(1001, 1001 + n),8 "date": pd.date_range("2024-01-01", periods=n, freq="D"),9 "city": np.random.choice(["Hà Nội", "TP.HCM", "Đà Nẵng", "Cần Thơ"], n),10 "category": np.random.choice(["Electronics", "Clothing", "Food", "Books"], n),11 "quantity": np.random.randint(1, 10, n),12 "unit_price": np.random.choice([50, 100, 200, 500, 1000], n),13})14df["revenue"] = df["quantity"] * df["unit_price"]1516print(df.shape)17print(df.head())18print(df.info())19print(df.describe())Bài 1.2: Khám phá nhanh
Đề bài: Trả lời các câu hỏi về dataset
1# (a) Có bao nhiêu đơn hàng mỗi thành phố?2print(df['city'].value_counts())34# (b) Category nào có doanh thu cao nhất?5print(df.groupby('category')['revenue'].sum().sort_values(ascending=False))67# (c) Đơn hàng lớn nhất (revenue)?8print(df.loc[df['revenue'].idxmax()])910# (d) Có bao nhiêu % đơn hàng > 1000?11pct = (df['revenue'] > 1000).mean() * 10012print(f"Đơn hàng > 1000: {pct:.1f}%")Checkpoint
Bạn đã nắm được workflow: shape → head → info → describe → value_counts chưa?
🟡 Phần 2: Selection và Filtering (Medium)
Ôn lại lý thuyết (Bài 6):
- Chọn cột:
df['col'],df[['col1', 'col2']] - Chọn dòng:
df.loc[index],df.iloc[position] - Boolean Indexing:
df[df['col'] > 10] - Kết hợp điều kiện:
&(AND),|(OR),~(NOT) → nhớ dùng()! - Helpers:
isin(),between(),query(),str.contains() - Tạo cột mới:
df['new'] = df['a'] + df['b'],pd.cut(),dt.month
Bài 2.1: Lọc đơn hàng
Đề bài: Lọc theo nhiều điều kiện khác nhau
1# (a) Đơn hàng tại Hà Nội, category Electronics2hn_elec = df[(df['city'] == 'Hà Nội') & (df['category'] == 'Electronics')]3print(f"HN Electronics: {len(hn_elec)} đơn")45# (b) Đơn hàng revenue từ 500-20006mid_range = df[df['revenue'].between(500, 2000)]7print(f"Revenue 500-2000: {len(mid_range)} đơn")89# (c) Đơn ở HN hoặc TP.HCM, quantity ≥ 510big_orders = df[11 (df['city'].isin(['Hà Nội', 'TP.HCM'])) & 12 (df['quantity'] >= 5)13]14print(f"Big orders HN/HCM: {len(big_orders)} đơn")1516# (d) Dùng query()17result = df.query('city == "Đà Nẵng" and revenue > 1000')18print(f"DN revenue > 1000: {len(result)} đơn")Bài 2.2: Tạo cột mới
Đề bài: Thêm các cột phân tích
1# (a) Cột month và weekday2df['month'] = df['date'].dt.month3df['weekday'] = df['date'].dt.day_name()45# (b) Revenue tier6df['tier'] = pd.cut(df['revenue'], 7 bins=[0, 200, 1000, 5000, float('inf')],8 labels=['Small', 'Medium', 'Large', 'Premium'])910# (c) Running total (cumulative)11df_sorted = df.sort_values('date')12df_sorted['cumulative_revenue'] = df_sorted['revenue'].cumsum()1314print(df[['order_id', 'revenue', 'month', 'tier']].head(10))Checkpoint
Boolean indexing trong Pandas tương tự NumPy nhưng dùng & thay cho and, | thay cho or. Bạn đã nhớ dùng ngoặc () chưa?
🟡 Phần 3: Groupby Analysis (Medium)
Ôn lại lý thuyết (Bài 6):
- Groupby: Split-Apply-Combine paradigm
.groupby('col').agg(): Tổng hợp nhiều cách (sum,mean,count,max...).groupby('col').transform(): Giữ nguyên shape, broadcast kết quả- Groupby 2+ cột:
groupby(['col1', 'col2']) reset_index(): Chuyển MultiIndex thành cột thường- Time series:
pd.date_range(),dt.month,dt.day_name(),pct_change()
Bài 3.1: Phân tích theo thành phố
Đề bài: Tổng hợp doanh thu các thành phố
1city_stats = df.groupby('city').agg(2 total_revenue = ('revenue', 'sum'),3 avg_revenue = ('revenue', 'mean'),4 total_orders = ('order_id', 'count'),5 avg_quantity = ('quantity', 'mean')6).round(0)78# Sắp xếp theo tổng doanh thu9city_stats = city_stats.sort_values('total_revenue', ascending=False)10print(city_stats)1112# Thêm cột % doanh thu13city_stats['revenue_pct'] = (14 city_stats['total_revenue'] / city_stats['total_revenue'].sum() * 10015).round(1)16print(city_stats)Bài 3.2: Cross-analysis (City × Category)
Đề bài: Phân tích chéo thành phố và danh mục
1# Groupby 2 cột2cross = df.groupby(['city', 'category']).agg(3 revenue = ('revenue', 'sum'),4 orders = ('order_id', 'count')5).reset_index()67# Top category mỗi thành phố8top_per_city = cross.loc[9 cross.groupby('city')['revenue'].idxmax()10]11print("Top category per city:")12print(top_per_city[['city', 'category', 'revenue']])1314# Transform: % đóng góp trong mỗi thành phố15cross['city_pct'] = cross.groupby('city')['revenue'].transform(16 lambda x: x / x.sum() * 10017).round(1)18print(cross)Bài 3.3: Time Series Analysis
Đề bài: Phân tích xu hướng doanh thu theo thời gian
1# Doanh thu theo tháng2monthly = df.groupby('month').agg(3 revenue = ('revenue', 'sum'),4 orders = ('order_id', 'count'),5 avg_order_value = ('revenue', 'mean')6).round(0)7print(monthly)89# Tăng trưởng so tháng trước10monthly['growth_pct'] = monthly['revenue'].pct_change() * 10011print(monthly[['revenue', 'growth_pct']].round(1))1213# Doanh thu theo ngày trong tuần14weekday_order = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 15 'Friday', 'Saturday', 'Sunday']16weekday_rev = df.groupby('weekday')['revenue'].mean()17weekday_rev = weekday_rev.reindex(weekday_order)18print(weekday_rev.round(0))Checkpoint
Bạn đã phân biệt được .agg() vs .transform() chưa? .agg() trả về kết quả đã gộp, .transform() giữ nguyên shape.
🔴 Phần 4: Merge và Pivot (Hard)
Ôn lại lý thuyết (Bài 6):
- Merge: Nối bảng giống SQL JOIN
pd.merge(df1, df2, on='key', how='left/right/inner/outer')how='left': giữ tất cả dòng df1,how='inner': chỉ giữ khớp
- Pivot Table: Chuyển dữ liệu dạng dài → rộng (Excel Pivot)
pivot_table(values, index, columns, aggfunc)margins=True: thêm dòng/cột tổng
- RFM: Recency (mới mua), Frequency (tần suất), Monetary (giá trị)
Bài 4.1: Multi-table Analysis
Đề bài: Merge 3 bảng — orders + products + customers
1# Tạo bảng Products2products = pd.DataFrame({3 "product_id": [1, 2, 3, 4, 5],4 "product_name": ["Laptop", "Phone", "Tablet", "Headphones", "Keyboard"],5 "cost": [800, 500, 300, 50, 30]6})78# Tạo bảng Customers9customers = pd.DataFrame({10 "customer_id": range(1, 11),11 "name": [f"Customer_{i}" for i in range(1, 11)],12 "segment": np.random.choice(["Gold", "Silver", "Bronze"], 10)13})1415# Tạo bảng Orders16np.random.seed(42)17orders = pd.DataFrame({18 "order_id": range(1, 51),19 "customer_id": np.random.randint(1, 11, 50),20 "product_id": np.random.randint(1, 6, 50),21 "quantity": np.random.randint(1, 5, 50),22 "unit_price": np.random.choice([100, 200, 500, 1000, 1500], 50)23})2425# Merge 3 bảng26full = (orders27 .merge(products, on='product_id')28 .merge(customers, on='customer_id'))2930# Tính profit31full['revenue'] = full['quantity'] * full['unit_price']32full['profit'] = full['revenue'] - full['quantity'] * full['cost']3334# Phân tích: Top customers by profit35top_customers = (full.groupby(['customer_id', 'name', 'segment'])36 .agg(total_orders=('order_id', 'count'),37 total_revenue=('revenue', 'sum'),38 total_profit=('profit', 'sum'))39 .sort_values('total_profit', ascending=False)40 .reset_index())41print(top_customers.head())Bài 4.2: Pivot Table Report
Đề bài: Tạo báo cáo Pivot tổng hợp
1# Pivot: Revenue theo segment × product2pivot = full.pivot_table(3 values='revenue',4 index='segment',5 columns='product_name',6 aggfunc='sum',7 fill_value=0,8 margins=True, # Thêm tổng dòng/cột9 margins_name='TOTAL'10)11print(pivot)1213# Pivot: Profit margin theo segment14margin_pivot = full.pivot_table(15 values=['revenue', 'profit'],16 index='segment',17 aggfunc='sum'18)19margin_pivot['margin_%'] = (margin_pivot['profit'] / margin_pivot['revenue'] * 100).round(1)20print(margin_pivot)Bài 4.3: RFM Analysis (Real-world)
Đề bài: Phân khúc khách hàng bằng RFM (Recency - Frequency - Monetary)
1# Tạo data mua hàng2np.random.seed(42)3n = 5004purchases = pd.DataFrame({5 "customer_id": np.random.randint(1, 51, n),6 "date": pd.date_range("2024-01-01", periods=n, freq="12H"),7 "amount": np.random.exponential(200, n).round(0)8})910# RFM11today = purchases['date'].max() + pd.Timedelta(days=1)1213rfm = purchases.groupby('customer_id').agg(14 recency = ('date', lambda x: (today - x.max()).days),15 frequency = ('customer_id', 'count'),16 monetary = ('amount', 'sum')17).round(0)1819# Scoring: chia mỗi metric thành 4 nhóm (1-4)20rfm['R_score'] = pd.qcut(rfm['recency'], 4, labels=[4, 3, 2, 1])21rfm['F_score'] = pd.qcut(rfm['frequency'].rank(method='first'), 4, labels=[1, 2, 3, 4])22rfm['M_score'] = pd.qcut(rfm['monetary'], 4, labels=[1, 2, 3, 4])2324# Tổng RFM score25rfm['RFM_score'] = (rfm['R_score'].astype(int) + 26 rfm['F_score'].astype(int) + 27 rfm['M_score'].astype(int))2829# Phân khúc30def segment(score):31 if score >= 10: return "Champion"32 elif score >= 8: return "Loyal"33 elif score >= 6: return "Potential"34 else: return "At Risk"3536rfm['segment'] = rfm['RFM_score'].apply(segment)37print(rfm['segment'].value_counts())38print(rfm.groupby('segment')[['recency', 'frequency', 'monetary']].mean().round(0))RFM Analysis là kỹ thuật phân khúc khách hàng phổ biến nhất trong marketing analytics. Bạn sẽ gặp lại trong các bài Challenge!
Checkpoint
Bạn đã hoàn thành bài RFM chưa? Dây là kỹ thuật phân tích rất quan trọng trong thực tế!
📝 Tổng Kết
Câu hỏi tự kiểm tra
- RFM Analysis gồm những chỉ số nào? Mỗi chỉ số đo lường điều gì về khách hàng?
- Khi merge nhiều bảng dữ liệu, làm sao biết nên dùng
how='inner'hayhow='left'? pd.qcut()khácpd.cut()như thế nào? Khi nào nên dùng mỗi loại?- Pivot table với
margins=Truethêm thông tin gì vào báo cáo?
✅ Checklist hoàn thành
- 🟢 Phần 1 (Easy): Tạo dataset, khám phá — xong?
- 🟡 Phần 2 (Medium): Selection, filtering, tạo cột mới — xong?
- 🟡 Phần 3 (Medium): Groupby city, cross-analysis, time series — xong?
- 🔴 Phần 4 (Hard): Multi-table merge, pivot report, RFM — xong?
Pandas Cheat Sheet
1# Read & Explore2df = pd.read_csv("file.csv")3df.shape, df.info(), df.describe()45# Filter6df[df['col'] > 10]7df.query('col > 10')89# Group & Aggregate10df.groupby('key').agg(total=('val', 'sum'))1112# Merge13pd.merge(df1, df2, on='key', how='left')1415# Pivot16df.pivot_table(values='val', index='row', columns='col', aggfunc='sum')Bài tiếp theo: Visualization — Trực quan hóa dữ liệu với Matplotlib, Seaborn, Plotly! 📈
