🎯 Mục tiêu bài học
Sau bài thực hành này, bạn sẽ:
✅ Tạo dashboard phân tích hoàn chỉnh bằng Seaborn
✅ Chọn đúng loại biểu đồ cho từng câu hỏi phân tích
✅ Customize biểu đồ chuyên nghiệp (colors, labels, annotations)
✅ Tạo interactive visualization với Plotly
Thời gian: 2 giờ | Độ khó: Beginner → Hard | Yêu cầu: Hoàn thành Bài 8 (Visualization)
🟢 Phần 1: Basic Charts (Easy)
Ôn lại lý thuyết (Bài 8):
- Matplotlib: Core library,
fig, ax = plt.subplots() - Seaborn: High-level, built on Matplotlib, themes + palettes
- Plotly: Interactive charts,
px.scatter(),px.line(),px.bar() - Chart types: Line (xu hướng), Bar (so sánh), Scatter (tương quan), Box (phân phối)
Bài 1.1: Tạo dataset và biểu đồ cơ bản
1import matplotlib.pyplot as plt2import seaborn as sns3import plotly.express as px4import pandas as pd5import numpy as np67np.random.seed(42)8sns.set_theme(style="whitegrid")910# Dataset: Doanh thu 12 tháng theo 3 khu vực11months = pd.date_range("2024-01-01", periods=12, freq="ME")12df = pd.DataFrame({13 "month": list(months) * 3,14 "region": ["Bắc"] * 12 + ["Trung"] * 12 + ["Nam"] * 12,15 "revenue": np.concatenate([16 np.random.normal(500, 50, 12).cumsum(),17 np.random.normal(400, 60, 12).cumsum(),18 np.random.normal(600, 40, 12).cumsum()19 ]).round(0)20})Bài 1.2: Line Chart — Xu hướng doanh thu
Đề bài: Vẽ line chart doanh thu theo tháng, mỗi khu vực 1 đường
1fig, ax = plt.subplots(figsize=(12, 6))23for region in df['region'].unique():4 data = df[df['region'] == region]5 ax.plot(data['month'], data['revenue'], marker='o', label=region)67ax.set_title("Doanh Thu Theo Tháng Theo Khu Vực", fontsize=14, fontweight='bold')8ax.set_xlabel("Tháng")9ax.set_ylabel("Doanh thu (triệu VNĐ)")10ax.legend(title="Khu vực")11ax.tick_params(axis='x', rotation=45)12plt.tight_layout()13plt.show()Bài 1.3: Bar Chart — So sánh tổng doanh thu
Đề bài: Bar chart tổng doanh thu mỗi khu vực
1totals = df.groupby('region')['revenue'].sum().sort_values(ascending=False)23fig, ax = plt.subplots(figsize=(8, 5))4colors = ['#FF6B6B', '#4ECDC4', '#45B7D1']5bars = ax.bar(totals.index, totals.values, color=colors, edgecolor='black')67# Thêm số trên mỗi bar8for bar in bars:9 height = bar.get_height()10 ax.text(bar.get_x() + bar.get_width()/2, height + 50,11 f'{height:,.0f}', ha='center', fontweight='bold')1213ax.set_title("Tổng Doanh Thu Theo Khu Vực", fontsize=14, fontweight='bold')14ax.set_ylabel("Doanh thu (triệu VNĐ)")15plt.tight_layout()16plt.show()Checkpoint
Bạn đã thêm title, labels, legend cho biểu đồ chưa? Biểu đồ không có label là biểu đồ vô nghĩa!
🟡 Phần 2: Seaborn Statistical Plots (Medium)
Ôn lại lý thuyết (Bài 8):
- Distribution plots:
histplot(KDE),boxplot(quartiles),violinplot(density + box) - Categorical:
barplot(mean + CI),countplot(count),stripplot(điểm rải) - Correlation:
heatmap(ma trận tương quan),regplot(scatter + regression) - Subplots:
fig, axes = plt.subplots(2, 2)rồi dùngax=axes[i,j] - melt(): Chuyển wide → long format cho Seaborn
Bài 2.1: Distribution Analysis
Đề bài: Phân tích phân phối điểm thi của 500 sinh viên
1np.random.seed(42)2scores = pd.DataFrame({3 "math": np.random.normal(72, 12, 500).clip(0, 100),4 "physics": np.random.normal(68, 15, 500).clip(0, 100),5 "chemistry": np.random.normal(75, 10, 500).clip(0, 100),6 "gender": np.random.choice(["Nam", "Nữ"], 500)7})89fig, axes = plt.subplots(2, 2, figsize=(14, 10))1011# (a) Histogram + KDE cho Toán12sns.histplot(scores, x="math", kde=True, ax=axes[0,0], color="steelblue")13axes[0,0].axvline(scores['math'].mean(), color='red', linestyle='--', label='Mean')14axes[0,0].legend()15axes[0,0].set_title("Phân phối điểm Toán")1617# (b) Box plot 3 môn18scores_melted = scores.melt(id_vars='gender', value_vars=['math','physics','chemistry'],19 var_name='subject', value_name='score')20sns.boxplot(data=scores_melted, x='subject', y='score', hue='gender', ax=axes[0,1])21axes[0,1].set_title("Điểm theo Môn và Giới tính")2223# (c) Violin plot24sns.violinplot(data=scores_melted, x='subject', y='score', 25 hue='gender', split=True, ax=axes[1,0])26axes[1,0].set_title("Violin Plot")2728# (d) Correlation heatmap29corr = scores[['math','physics','chemistry']].corr()30sns.heatmap(corr, annot=True, cmap='coolwarm', center=0, ax=axes[1,1], fmt='.2f')31axes[1,1].set_title("Tương quan giữa các môn")3233plt.suptitle("Phân Tích Điểm Thi Sinh Viên", fontsize=16, fontweight='bold')34plt.tight_layout()35plt.show()Bài 2.2: Categorical Analysis
Đề bài: Phân tích dữ liệu tips (dataset mẫu Seaborn)
1tips = sns.load_dataset("tips")23fig, axes = plt.subplots(1, 3, figsize=(18, 5))45# (a) Mean tip by day + smoker6sns.barplot(data=tips, x="day", y="tip", hue="smoker", 7 errorbar="sd", ax=axes[0], palette="Set2")8axes[0].set_title("Tip TB theo Ngày")910# (b) Count by day and time11sns.countplot(data=tips, x="day", hue="time", ax=axes[1], palette="Pastel1")12axes[1].set_title("Số lượng theo Ngày & Buổi")1314# (c) Scatter: bill vs tip with regression15sns.regplot(data=tips, x="total_bill", y="tip", ax=axes[2],16 scatter_kws={'alpha': 0.5}, color="coral")17axes[2].set_title("Bill vs Tip (Regression)")1819plt.tight_layout()20plt.show()Checkpoint
Bạn đã biết khi nào dùng histplot vs boxplot vs violinplot chưa?
🟡 Phần 3: Plotly Interactive (Medium)
Ôn lại lý thuyết (Bài 8):
- Plotly Express:
px.scatter(),px.line(),px.bar(),px.pie() - Interactive features: Zoom, pan, hover tooltip, filter legends
- Animation:
animation_frame="year"tạo slider thời gian - Customization:
color,size,hover_name,log_x,range_x - Advanced:
px.sunburst(),px.treemap()cho hierarchical data
Bài 3.1: Interactive Dashboard
Đề bài: Tạo các biểu đồ interactive với Plotly
1# Dataset: Gapminder2df = px.data.gapminder()34# (a) Scatter: GDP vs Life Expectancy (2007) — hover country, size pop5fig = px.scatter(df.query("year == 2007"),6 x="gdpPercap", y="lifeExp",7 color="continent", size="pop",8 hover_name="country", log_x=True,9 size_max=60,10 title="GDP vs Tuổi Thọ — 2007")11fig.show()1213# (b) Animated scatter qua các năm14fig = px.scatter(df, x="gdpPercap", y="lifeExp",15 animation_frame="year",16 animation_group="country",17 size="pop", color="continent",18 hover_name="country",19 log_x=True, size_max=55,20 range_x=[100, 100000], range_y=[25, 90],21 title="Gapminder Animation")22fig.show()2324# (c) Vietnam vs neighbors25countries = ['Vietnam', 'Thailand', 'Indonesia', 'Philippines', 'Malaysia']26df_sea = df[df['country'].isin(countries)]2728fig = px.line(df_sea, x="year", y="gdpPercap",29 color="country", markers=True,30 title="GDP Per Capita — Southeast Asia")31fig.show()Bài 3.2: Business Charts
Đề bài: Biểu đồ kinh doanh interactive
1# Pie chart: Market share2market = pd.DataFrame({3 "company": ["Apple", "Samsung", "Xiaomi", "OPPO", "Others"],4 "share": [27, 21, 14, 10, 28]5})6fig = px.pie(market, values="share", names="company",7 title="Thị phần Smartphone 2024",8 hole=0.4) # Donut9fig.show()1011# Sunburst: Hierarchical12tips = px.data.tips()13fig = px.sunburst(tips, path=["day", "time", "sex"],14 values="total_bill",15 title="Revenue Breakdown")16fig.show()Checkpoint
Plotly animation là tính năng rất ấn tượng trong presentation. Bạn đã tạo animation thành công chưa?
🔴 Phần 4: Complete Analysis Report (Hard)
Bài này kết hợp tất cả kỹ năng EDA:
- Chọn đúng chart cho từng câu hỏi (pie, bar, hist, box, heatmap)
- Layout subplot rõ ràng (3x2 grid)
- Color palette nhất quán
- Title, labels đầy đủ
- Lưu xuất PNG resolution cao (
dpi=300)
Tip: Xem solution rồi tự build dashboard cho dataset khác (housing, salary, etc.)
Bài 4.1: Full EDA Dashboard
Đề bài: Tạo báo cáo phân tích hoàn chỉnh cho dataset Titanic
1# Load data2titanic = sns.load_dataset("titanic")3print(titanic.shape)4print(titanic.head())56fig, axes = plt.subplots(3, 2, figsize=(14, 16))78# 1. Survival rate9survival = titanic['survived'].value_counts()10axes[0,0].pie(survival.values, labels=['Không sống sót', 'Sống sót'],11 autopct='%1.1f%%', colors=['#FF6B6B', '#4ECDC4'])12axes[0,0].set_title("Tỷ lệ sống sót")1314# 2. Survival by class15sns.barplot(data=titanic, x='class', y='survived', hue='sex', 16 ax=axes[0,1], palette='Set2')17axes[0,1].set_title("Tỷ lệ sống sót theo Hạng & Giới tính")18axes[0,1].set_ylabel("Tỷ lệ sống sót")1920# 3. Age distribution by survival21sns.histplot(data=titanic, x='age', hue='survived', kde=True,22 ax=axes[1,0], palette={0: '#FF6B6B', 1: '#4ECDC4'})23axes[1,0].set_title("Phân phối tuổi theo Sống sót")2425# 4. Fare distribution by class26sns.boxplot(data=titanic, x='class', y='fare', ax=axes[1,1], palette='Set3')27axes[1,1].set_title("Giá vé theo Hạng")2829# 5. Embarkation port30sns.countplot(data=titanic, x='embark_town', hue='survived',31 ax=axes[2,0], palette={0: '#FF6B6B', 1: '#4ECDC4'})32axes[2,0].set_title("Cảng lên tàu & Sống sót")3334# 6. Correlation35numeric = titanic.select_dtypes(include=[np.number])36sns.heatmap(numeric.corr(), annot=True, cmap='RdYlGn', center=0,37 ax=axes[2,1], fmt='.2f')38axes[2,1].set_title("Ma trận tương quan")3940plt.suptitle("🚢 TITANIC — PHÂN TÍCH DỮ LIỆU TOÀN DIỆN",41 fontsize=18, fontweight='bold', y=1.01)42plt.tight_layout()43plt.savefig("titanic_analysis.png", dpi=300, bbox_inches='tight')44plt.show()Template EDA Dashboard ở trên là pattern bạn sẽ dùng đi dùng lại trong mọi dự án Data Science. Hãy lưu lại làm template!
Checkpoint
Bạn đã tạo được dashboard 6 biểu đồ trên Titanic chưa? Kết luận gì về yếu tố ảnh hưởng sống sót?
📝 Tổng Kết
Câu hỏi tự kiểm tra
- Khi tạo EDA Dashboard, những loại biểu đồ nào nên có để phân tích toàn diện?
sns.pairplot()giúp bạn khám phá điều gì về dữ liệu? Khi nào nên sử dụng?- Plotly Express khác Matplotlib ở điểm nào khi tạo biểu đồ tương tác?
- Tại sao nên lưu biểu đồ với
dpi=300thay vì giá trị mặc định?
✅ Checklist hoàn thành
- 🟢 Phần 1 (Easy): Line chart, bar chart — xong?
- 🟡 Phần 2 (Medium): Seaborn statistical plots — xong?
- 🟡 Phần 3 (Medium): Plotly interactive — xong?
- 🔴 Phần 4 (Hard): Full EDA dashboard — xong?
Visualization Cheat Sheet
1# Seaborn (static, statistical)2sns.histplot(df, x="col", kde=True)3sns.boxplot(df, x="cat", y="num")4sns.scatterplot(df, x="x", y="y", hue="cat")5sns.heatmap(corr, annot=True)67# Plotly (interactive)8px.scatter(df, x="x", y="y", color="cat", size="val")9px.bar(df, x="cat", y="val")10px.line(df, x="time", y="val", color="cat")11px.pie(df, values="val", names="cat")Bài tiếp theo: Data Cleaning — Xử lý dữ liệu bẩn, bước quan trọng nhất trong Data Science! 🧹
