Skip to content

📘 Buổi 9: EDA — Khám phá dữ liệu và tìm insight

EDA = "khám sức khỏe tổng quát" cho dataset. Bước bắt buộc trước mọi phân tích — bỏ qua EDA là lao vào phân tích với đôi mắt nhắm nghiền.

🎯 Mục tiêu buổi học

Sau buổi này, học viên sẽ:

  1. Thực hiện quy trình EDA hoàn chỉnh: univariate → bivariate → multivariate
  2. Tạo distribution plots, scatter plots, correlation matrix
  3. Phát hiện patterns, anomalies, relationships trong dữ liệu
  4. Viết EDA summary với insight rõ ràng, actionable

📋 Tổng quan

Ở Buổi 8, bạn đã thành thạo Pandas & Numpy — đọc file, xử lý missing values, xóa duplicates, merge DataFrames. Dữ liệu đã sạch, nhưng sạch chưa có nghĩa là hiểu. Bạn có 500.000 dòng order data — giá trị trung bình là bao nhiêu? Phân phối có bị lệch không? Cột nào ảnh hưởng mạnh nhất đến doanh thu? Có outlier nào đang phá hỏng phân tích? Đó chính là câu hỏi mà EDA — Exploratory Data Analysis trả lời.

EDA là quá trình khám phá dữ liệu một cách có hệ thống — quan sát, tóm tắt, trực quan hóa — để hiểu cấu trúc, phát hiện pattern và anomaly trước khi đưa ra kết luận hoặc xây dựng model. John Tukey, cha đẻ EDA, từng nói: "The greatest value of a picture is when it forces us to notice what we never expected to see." — biểu đồ tốt sẽ buộc bạn nhìn thấy điều bạn không ngờ tới.

Nhớ lại hành trình OSEMN: Buổi 7 bạn Obtain dữ liệu bằng Python, Buổi 8 bạn Scrub bằng Pandas. Buổi 9 là bước Explore — bước mà Data Analyst thật sự bắt đầu "nói chuyện" với dữ liệu.

mermaid
flowchart LR
    A["📥 Obtain<br/>Buổi 7: Python đọc file"] --> B["🧹 Scrub<br/>Buổi 8: Pandas Data Cleaning"]
    B --> C["🔍 Explore<br/>✅ Buổi 9: EDA"]
    C --> D["📊 iNterpret<br/>Buổi 10-11: Visualization"]
    D --> E["🤖 Model<br/>Buổi 12+"]
    style C fill:#e8f5e9,stroke:#4caf50,stroke-width:3px

💡 EDA trong thực tế doanh nghiệp

Ai cần EDA?Tình huốngOutput kỳ vọng
DA kiểm tra data mới nhậnNhận file 200K dòng từ team vận hànhSummary: shape, dtypes, missing %, phân phối
Product Manager hỏi trend"Doanh thu Q4 có gì bất thường?"Time series plot + highlight anomaly
Marketing muốn segment"Ai là khách hàng VIP?"Distribution + scatter plot spending vs frequency
DS trước khi build modelKiểm tra feature relationshipsCorrelation matrix, feature importance

Công cụ sử dụng: Jupyter Notebook, Pandas, Matplotlib, Seaborn — tất cả công cụ bạn cần để biến data thành hình ảnh có ý nghĩa.


📌 Phần 1: EDA Framework — Quy trình khám phá dữ liệu

OSEMN Pipeline — Focus vào bước Explore

EDA không phải "nhìn data rồi nghĩ ra insight" — nó là quy trình có cấu trúc. Mỗi bước dẫn đến bước tiếp theo, từ tổng quát đến chi tiết:

mermaid
flowchart TD
    A["1️⃣ Overview<br/>shape, dtypes, head, info"] --> B["2️⃣ Univariate<br/>Phân tích từng biến riêng lẻ"]
    B --> C["3️⃣ Bivariate<br/>Mối quan hệ giữa 2 biến"]
    C --> D["4️⃣ Multivariate<br/>Nhiều biến cùng lúc"]
    D --> E["5️⃣ Summary<br/>Insight + recommendation"]

Bước đầu tiên — Dataset Overview

Mọi EDA đều bắt đầu bằng 5 lệnh "must-run" để hiểu dataset tổng quan trước khi đi sâu:

python
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

# Thiết lập style cho biểu đồ
sns.set_theme(style="whitegrid")
plt.rcParams["figure.figsize"] = (10, 6)

# Đọc dataset e-commerce
df = pd.read_csv("ecommerce_orders.csv")

# 5 lệnh overview — chạy MỌI LÚC khi nhận data mới
print(f"Shape: {df.shape[0]:,} dòng × {df.shape[1]} cột")
# Shape: 50,000 dòng × 12 cột

print(df.head())
print(df.info())
print(df.describe())
print(df.dtypes)
python
# Kiểm tra missing values — bao nhiêu %, cột nào bị thiếu
missing = df.isnull().sum()
missing_pct = (missing / len(df) * 100).round(2)
missing_report = pd.DataFrame({
    "missing_count": missing,
    "missing_pct": missing_pct
}).query("missing_count > 0").sort_values("missing_pct", ascending=False)

print(missing_report)
# 	              missing_count  missing_pct
# review_score            3500         7.00
# shipping_date           1200         2.40
# discount                 800         1.60

📌 Quy tắc nhanh xử lý missing trong EDA

  • < 5% missing → điền median (numerical) hoặc mode (categorical), hoặc dropna
  • 5–30% missing → phân tích pattern trước, dùng fillna có suy nghĩ
  • > 30% missing → cân nhắc drop cả cột — cột đó không đáng tin

Phân loại biến — Bước quan trọng trước khi phân tích

Hiểu loại biến quyết định bạn dùng biểu đồ nào, phép tính nào:

python
# Phân loại cột tự động
numerical_cols = df.select_dtypes(include=["int64", "float64"]).columns.tolist()
categorical_cols = df.select_dtypes(include=["object", "category"]).columns.tolist()

print(f"Numerical ({len(numerical_cols)}): {numerical_cols}")
# Numerical (6): ['order_value', 'quantity', 'discount', 'shipping_fee',
#                  'review_score', 'customer_age']

print(f"Categorical ({len(categorical_cols)}): {categorical_cols}")
# Categorical (5): ['order_id', 'customer_id', 'product_category',
#                    'city', 'payment_method']

# Kiểm tra cardinality — số giá trị unique của cột categorical
for col in categorical_cols:
    print(f"{col}: {df[col].nunique()} unique values")
# product_category: 15 unique values
# city: 63 unique values
# payment_method: 4 unique values

💡 So sánh với Excel

Thao tácExcelPandas EDA
Xem data nhanhScroll bảngdf.head(), df.sample(5)
Tổng quan thống kêDescriptive Statistics add-indf.describe()
Đếm missingCOUNTBLANK từng cộtdf.isnull().sum() — tất cả cột 1 dòng
Phân loại biếnTự nhìn từng cộtdf.select_dtypes() — tự động

📌 Phần 2: Univariate Analysis — Phân tích từng biến

Univariate = phân tích 1 biến tại một thời điểm. Mục tiêu: hiểu phân phối (distribution), central tendency (mean, median, mode), và các anomaly (outlier, skewness).

Numerical Variables — Histogram, Box Plot, KDE

python
# Histogram — xem phân phối order_value
fig, axes = plt.subplots(1, 3, figsize=(18, 5))

# 1. Histogram
axes[0].hist(df["order_value"], bins=50, color="steelblue", edgecolor="white")
axes[0].set_title("Distribution of Order Value")
axes[0].set_xlabel("Order Value (VND)")
axes[0].axvline(df["order_value"].mean(), color="red", linestyle="--", label=f'Mean: {df["order_value"].mean():,.0f}')
axes[0].axvline(df["order_value"].median(), color="green", linestyle="--", label=f'Median: {df["order_value"].median():,.0f}')
axes[0].legend()

# 2. Box Plot — phát hiện outlier
axes[1].boxplot(df["order_value"].dropna(), vert=True)
axes[1].set_title("Box Plot — Order Value")

# 3. KDE (Kernel Density Estimation) — đường cong phân phối mượt
sns.kdeplot(df["order_value"], ax=axes[2], fill=True, color="coral")
axes[2].set_title("KDE — Order Value")

plt.tight_layout()
plt.show()
python
# Thống kê mô tả chi tiết — vượt xa describe()
col = "order_value"
stats = {
    "count": df[col].count(),
    "mean": df[col].mean(),
    "median": df[col].median(),
    "std": df[col].std(),
    "skewness": df[col].skew(),      # > 0: lệch phải, < 0: lệch trái
    "kurtosis": df[col].kurtosis(),  # > 3: nhọn (nhiều outlier), < 3: phẳng
    "min": df[col].min(),
    "max": df[col].max(),
    "Q1": df[col].quantile(0.25),
    "Q3": df[col].quantile(0.75),
    "IQR": df[col].quantile(0.75) - df[col].quantile(0.25)
}
for k, v in stats.items():
    print(f"{k:>12}: {v:>15,.2f}")
#        count:       50,000.00
#         mean:    1,250,000.50
#       median:      980,000.00
#          std:      850,000.30
#     skewness:            1.85   ← Lệch phải — có outlier giá trị cao
#     kurtosis:            4.20   ← Nhọn — tail dài (nhiều outlier)

⚠️ Skewness — Khi Mean ≠ Median

Nếu skewness > 1 hoặc < -1 → phân phối lệch mạnh. Khi đó:

  • Không dùng Mean để đại diện → dùng Median thay thế
  • Histogram sẽ bất đối xứng, box plot sẽ có nhiều outlier dots
  • Cân nhắc log transform trước khi phân tích: df["log_value"] = np.log1p(df["order_value"])

Categorical Variables — Bar Chart, Value Counts

python
# Phân tích biến categorical — product_category
fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# 1. Bar chart — top 10 category phổ biến
top_cats = df["product_category"].value_counts().head(10)
top_cats.plot(kind="barh", ax=axes[0], color="steelblue")
axes[0].set_title("Top 10 Product Categories")
axes[0].set_xlabel("Number of Orders")

# 2. Payment method — pie chart (khi ít category)
df["payment_method"].value_counts().plot(
    kind="pie", ax=axes[1], autopct="%1.1f%%",
    colors=["#4CAF50", "#2196F3", "#FF9800", "#F44336"]
)
axes[1].set_title("Payment Method Distribution")
axes[1].set_ylabel("")

plt.tight_layout()
plt.show()
python
# value_counts chi tiết — tỷ lệ % + cumulative %
cat_analysis = df["product_category"].value_counts()
cat_pct = df["product_category"].value_counts(normalize=True) * 100
cat_cumsum = cat_pct.cumsum()

cat_summary = pd.DataFrame({
    "count": cat_analysis,
    "pct": cat_pct.round(2),
    "cumulative_pct": cat_cumsum.round(2)
})
print(cat_summary.head(8))
#                   count    pct  cumulative_pct
# Điện thoại         8500  17.00           17.00
# Thời trang         7200  14.40           31.40
# Điện tử            6800  13.60           45.00
# Gia dụng           5500  11.00           56.00
# Mỹ phẩm            4200   8.40           64.40
# Thực phẩm          3800   7.60           72.00
# Sách                3100   6.20           78.20  ← 80% đơn hàng tập trung ở 7 loại
# Thể thao            2900   5.80           84.00
📖 Pareto Principle trong EDA

Nhìn cột cumulative_pct: ~80% đơn hàng đến từ 7/15 category (= 47% category). Đây là Pareto Principle (80/20) — tập trung phân tích sâu 7 category này sẽ cover phần lớn business. Trong Excel, bạn sẽ cần tạo PivotTable + sort + thêm cột tính % cộng dồn thủ công.


📌 Phần 3: Bivariate & Multivariate Analysis — Mối quan hệ giữa các biến

Bivariate = 2 biến, multivariate = nhiều biến. Đây là lúc bạn tìm được insight thực sự — biến nào ảnh hưởng biến nào, customer segment nào chi nhiều nhất, feature nào liên quan mạnh đến target.

Numerical vs Numerical — Scatter Plot & Correlation

python
# Scatter plot — order_value vs quantity
fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# 1. Scatter plot cơ bản
axes[0].scatter(df["quantity"], df["order_value"],
                alpha=0.3, s=10, color="steelblue")
axes[0].set_xlabel("Quantity")
axes[0].set_ylabel("Order Value (VND)")
axes[0].set_title("Order Value vs Quantity")

# 2. Scatter + regression line (seaborn)
sns.regplot(x="customer_age", y="order_value", data=df,
            ax=axes[1], scatter_kws={"alpha": 0.2, "s": 10},
            line_kws={"color": "red"})
axes[1].set_title("Order Value vs Customer Age")

plt.tight_layout()
plt.show()
python
# Correlation — Pearson vs Spearman
# Pearson: tương quan tuyến tính (giả định phân phối chuẩn)
# Spearman: tương quan thứ bậc (không cần phân phối chuẩn, bền với outlier)

pearson_corr = df[numerical_cols].corr(method="pearson")
spearman_corr = df[numerical_cols].corr(method="spearman")

print("=== Pearson Correlation ===")
print(pearson_corr.round(3))
#                order_value  quantity  discount  shipping_fee  review_score  customer_age
# order_value          1.000     0.720    -0.150         0.450         0.120         0.050
# quantity             0.720     1.000    -0.080         0.380         0.090         0.030
# discount            -0.150    -0.080     1.000        -0.050        -0.020         0.010
# shipping_fee         0.450     0.380    -0.050         1.000         0.060         0.020
# review_score         0.120     0.090    -0.020         0.060         1.000         0.180
# customer_age         0.050     0.030     0.010         0.020         0.180         1.000

📌 Đọc Correlation — Quy tắc nhanh

Giá trịÝ nghĩaVí dụ
0.7 – 1.0Tương quan mạnhorder_valuequantity (0.72) → mua nhiều = giá trị cao
0.4 – 0.7Tương quan trung bìnhorder_valueshipping_fee (0.45)
0.0 – 0.4Tương quan yếu / không cócustomer_ageorder_value (0.05) → tuổi không ảnh hưởng
Giá trị âmTương quan nghịchdiscountorder_value (-0.15) → chiết khấu ↑ giá trị ↓

⚠️ Correlation ≠ Causation — tương quan không phải nhân quả! Ice cream sales tương quan với drowning rate, nhưng kem không gây đuối nước (cả hai tăng vào mùa hè).

Correlation Matrix Heatmap

python
# Heatmap — ma trận tương quan (biểu đồ DA dùng nhiều nhất)
plt.figure(figsize=(10, 8))
mask = np.triu(np.ones_like(pearson_corr, dtype=bool))  # Ẩn nửa trên (trùng lặp)

sns.heatmap(pearson_corr, mask=mask, annot=True, fmt=".2f",
            cmap="RdBu_r", center=0, vmin=-1, vmax=1,
            square=True, linewidths=0.5)
plt.title("Correlation Matrix — E-commerce Orders", fontsize=14)
plt.tight_layout()
plt.show()

Numerical vs Categorical — Box Plot Comparison

python
# So sánh order_value giữa các payment_method
fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# 1. Box plot comparison
sns.boxplot(x="payment_method", y="order_value", data=df, ax=axes[0],
            palette="Set2")
axes[0].set_title("Order Value by Payment Method")
axes[0].set_ylabel("Order Value (VND)")

# 2. Violin plot — chi tiết hơn box plot (thấy phân phối)
sns.violinplot(x="payment_method", y="order_value", data=df, ax=axes[1],
               palette="Set2", inner="quartile")
axes[1].set_title("Order Value Distribution by Payment Method")

plt.tight_layout()
plt.show()

# Thống kê nhóm — giống PivotTable trong Excel
group_stats = df.groupby("payment_method")["order_value"].agg(
    ["count", "mean", "median", "std"]
).round(0)
print(group_stats)
#                  count       mean     median        std
# COD              12500  1050000.0   850000.0   720000.0
# Credit Card      15000  1450000.0  1200000.0   950000.0
# E-wallet         18000  1100000.0   900000.0   780000.0
# Bank Transfer     4500  1800000.0  1500000.0  1100000.0

Categorical vs Categorical — Crosstab & Heatmap

python
# Crosstab — giống PivotTable đếm trong Excel
ct = pd.crosstab(df["product_category"], df["payment_method"],
                 normalize="index") * 100  # % theo dòng
ct = ct.round(1)

# Heatmap cho crosstab
plt.figure(figsize=(10, 8))
sns.heatmap(ct.head(8), annot=True, fmt=".1f", cmap="YlOrRd",
            cbar_kws={"label": "% of orders"})
plt.title("Payment Method by Product Category (%)")
plt.ylabel("Product Category")
plt.xlabel("Payment Method")
plt.tight_layout()
plt.show()

💡 So sánh tương đương Excel

Phân tíchExcelPandas
CorrelationData Analysis ToolPak → Correlationdf.corr()
Scatter plotInsert Chart → Scatterplt.scatter() / sns.regplot()
Box plot so sánhKhông trực quansns.boxplot(x=cat, y=num)
PivotTable đếmPivotTable → Countpd.crosstab()
Conditional formattingFormat → Color Scalessns.heatmap()

📌 Phần 4: Patterns & Anomalies — Phát hiện bất thường

Nếu Phần 1–3 giúp bạn mô tả dữ liệu, Phần 4 giúp bạn phát hiện điều bất thường — outlier đang kéo lệch trung bình, trend ẩn giấu, mùa vụ trong doanh thu, missing data có pattern hay ngẫu nhiên.

Outlier Detection — Visual + Statistical

python
# Phương pháp IQR — chuẩn thống kê phổ biến nhất
def detect_outliers_iqr(series, multiplier=1.5):
    """Phát hiện outlier bằng IQR method"""
    Q1 = series.quantile(0.25)
    Q3 = series.quantile(0.75)
    IQR = Q3 - Q1
    lower = Q1 - multiplier * IQR
    upper = Q3 + multiplier * IQR
    outliers = series[(series < lower) | (series > upper)]
    return outliers, lower, upper

outliers, lower, upper = detect_outliers_iqr(df["order_value"])
print(f"Outlier boundaries: [{lower:,.0f}{upper:,.0f}]")
print(f"Number of outliers: {len(outliers):,} ({len(outliers)/len(df)*100:.1f}%)")
# Outlier boundaries: [50,000 — 3,200,000]
# Number of outliers: 2,350 (4.7%)

# Visualize outlier
fig, axes = plt.subplots(1, 2, figsize=(16, 5))

# Box plot với outlier highlight
sns.boxplot(y=df["order_value"], ax=axes[0], color="lightblue")
axes[0].axhline(upper, color="red", linestyle="--", label=f"Upper: {upper:,.0f}")
axes[0].axhline(lower, color="red", linestyle="--", label=f"Lower: {lower:,.0f}")
axes[0].legend()
axes[0].set_title("Outlier Detection — IQR Method")

# Histogram with vs without outlier
df_no_outlier = df[(df["order_value"] >= lower) & (df["order_value"] <= upper)]
axes[1].hist(df_no_outlier["order_value"], bins=50, color="steelblue",
             edgecolor="white", alpha=0.7, label="Normal")
axes[1].hist(outliers, bins=20, color="red", edgecolor="white",
             alpha=0.5, label="Outliers")
axes[1].legend()
axes[1].set_title("Distribution — Normal vs Outliers")

plt.tight_layout()
plt.show()

⚠️ Outlier — Xóa hay giữ?

  • Xóa khi: lỗi nhập liệu (đơn hàng -500.000 VND), test data, data corruption
  • Giữ khi: giá trị thật (khách VIP mua 50 triệu), là insight quan trọng
  • Tách riêng khi: phân tích 2 nhóm — normal customers vs VIP/whale customers

Quy tắc: Luôn điều tra nguyên nhân trước khi quyết định xóa hay giữ. Outlier có thể là bug hoặc là vàng.

Trend & Seasonality Detection

python
# Phân tích trend theo thời gian — doanh thu theo tháng
df["order_date"] = pd.to_datetime(df["order_date"])
monthly = df.groupby(df["order_date"].dt.to_period("M")).agg(
    total_revenue=("order_value", "sum"),
    order_count=("order_id", "count"),
    avg_order_value=("order_value", "mean")
).reset_index()
monthly["order_date"] = monthly["order_date"].dt.to_timestamp()

fig, ax1 = plt.subplots(figsize=(14, 6))

# Doanh thu (trục trái)
ax1.plot(monthly["order_date"], monthly["total_revenue"] / 1e9,
         color="steelblue", marker="o", linewidth=2, label="Revenue (tỷ VND)")
ax1.set_ylabel("Revenue (tỷ VND)", color="steelblue")
ax1.set_xlabel("Month")

# Số đơn (trục phải)
ax2 = ax1.twinx()
ax2.bar(monthly["order_date"], monthly["order_count"],
        alpha=0.3, color="coral", width=20, label="Order Count")
ax2.set_ylabel("Order Count", color="coral")

ax1.set_title("Monthly Revenue & Order Count — Trend Analysis")
fig.legend(loc="upper left", bbox_to_anchor=(0.12, 0.88))
plt.tight_layout()
plt.show()
# Insight: Doanh thu peak tháng 11-12 (mùa sale cuối năm), dip tháng 2 (Tết)

Missing Data Patterns

python
# Missing data có pattern hay ngẫu nhiên?
# MCAR: Missing Completely At Random — ngẫu nhiên hoàn toàn
# MAR:  Missing At Random — thiếu phụ thuộc vào biến khác
# MNAR: Missing Not At Random — thiếu phụ thuộc chính giá trị bị thiếu

# Visualize missing pattern
import matplotlib.pyplot as plt

fig, axes = plt.subplots(1, 2, figsize=(16, 6))

# 1. Missing matrix — mỗi dòng = 1 row, trắng = NaN
axes[0].imshow(df.isnull().values[:200], aspect="auto", cmap="gray_r",
               interpolation="none")
axes[0].set_title("Missing Data Pattern (200 rows)")
axes[0].set_xlabel("Columns")
axes[0].set_yticks([])

# 2. Missing correlation — cột nào bị missing cùng lúc?
missing_corr = df.isnull().corr()
sns.heatmap(missing_corr, annot=True, fmt=".2f", ax=axes[1],
            cmap="RdBu_r", center=0)
axes[1].set_title("Missing Value Correlation")

plt.tight_layout()
plt.show()
# Insight: review_score và shipping_date có missing correlation 0.65
# → khi đơn chưa giao (thiếu shipping_date), khách chưa review (thiếu review_score)
# → Đây là MAR, không phải MCAR — missing có logic business!

EDA Summary — Viết insight actionable

python
# Template EDA Summary — output cuối cùng của mọi EDA
eda_summary = """
========================================
       EDA SUMMARY — E-commerce Orders
========================================

📊 DATASET OVERVIEW:
- 50,000 orders | 12 columns | Period: 01/2025 – 12/2025
- Missing: review_score (7%), shipping_date (2.4%), discount (1.6%)

📈 KEY FINDINGS:
1. Order Value: median 980K VND, right-skewed (skew=1.85)
   → Dùng median, KHÔNG dùng mean khi báo cáo "đơn hàng trung bình"

2. Top 3 categories (Điện thoại, Thời trang, Điện tử) = 45% tổng đơn
   → Pareto — focus marketing vào 3 category này

3. Correlation: order_value ↔ quantity = 0.72 (mạnh)
   → Khuyến khích mua nhiều sản phẩm = tăng giá trị đơn hàng

4. Payment: Bank Transfer có avg order value cao nhất (1.8M)
   → Đơn hàng giá trị cao prefer chuyển khoản

5. Seasonality: Peak tháng 11-12, dip tháng 2
   → Chuẩn bị inventory & nhân sự cho Q4

⚠️ ANOMALIES:
- 4.7% outlier (> 3.2M VND) — cần kiểm tra: VIP hay lỗi data?
- Missing review_score correlate với missing shipping_date (MAR)

🎯 RECOMMENDATIONS:
- Segment khách hàng theo order_value (VIP > 3M, Standard, Low)
- Phân tích sâu chênh lệch payment method
- Build prediction model cho monthly revenue (có seasonality rõ)
"""
print(eda_summary)
📖 Checklist EDA hoàn chỉnh — Copy để dùng lại
□ Shape, dtypes, head() — overview dataset
□ Missing values — count, %, pattern (MCAR/MAR/MNAR)
□ Duplicates check
□ Univariate — histogram + boxplot cho mỗi numerical column
□ Univariate — value_counts + bar chart cho mỗi categorical column
□ describe() + skew() + kurtosis() cho numerical
□ Bivariate — scatter plot cho top correlated pairs
□ Correlation matrix + heatmap
□ Box plot comparison (numerical vs categorical)
□ Crosstab (categorical vs categorical) nếu cần
□ Time series trend (nếu có date column)
□ Outlier detection — IQR method
□ EDA Summary — key findings + recommendations

🔑 Từ khóa chính

Tiếng ViệtEnglishGiải thích
Phân tích khám pháEDA (Exploratory Data Analysis)Khám phá data trước khi phân tích chính — descriptive, visual
Tương quanCorrelationMức độ liên hệ giữa 2 biến — Pearson (tuyến tính), Spearman (thứ bậc)
Phân phốiDistributionCách data phân bố — histogram, KDE, skewness
Ma trận tương quanCorrelation MatrixBảng tương quan giữa tất cả cặp biến — visualize bằng heatmap
InsightInsightPhát hiện có giá trị từ dữ liệu — phải actionable, không chỉ mô tả
Phân tích đơn biếnUnivariate AnalysisPhân tích 1 biến riêng lẻ — histogram, box plot, value_counts
Phân tích hai biếnBivariate AnalysisPhân tích mối quan hệ giữa 2 biến — scatter, box plot comparison
Giá trị ngoại laiOutlierGiá trị bất thường, nằm ngoài phạm vi bình thường — IQR method
Độ lệchSkewnessMức độ lệch của phân phối — >0 lệch phải, <0 lệch trái
Xu hướngTrendPattern thay đổi theo thời gian — tăng, giảm, mùa vụ

📊 Tổng kết buổi học

✅ Checklist — Bạn đã nắm được

  • [ ] Thực hiện dataset overview: shape, info, describe, dtypes, isnull().sum()
  • [ ] Phân loại biến: numerical vs categorical bằng select_dtypes()
  • [ ] Univariate analysis: histogram, box plot, KDE cho numerical
  • [ ] Univariate analysis: bar chart, value_counts, pie chart cho categorical
  • [ ] Tính skew(), kurtosis() và hiểu ý nghĩa
  • [ ] Bivariate: scatter plot, corr() (Pearson & Spearman)
  • [ ] Correlation matrix + heatmap với Seaborn
  • [ ] Box plot comparison: numerical vs categorical
  • [ ] Crosstab: categorical vs categorical
  • [ ] Outlier detection bằng IQR method
  • [ ] Trend & seasonality detection với time series plot
  • [ ] Missing data pattern analysis (MCAR, MAR, MNAR)
  • [ ] Viết EDA summary với insight actionable

🗺️ Hành trình học tập

Buổi 3-4: Excel — Pivot Table, biểu đồ cơ bản

Buổi 5-6: SQL — Query, JOIN, GROUP BY, Window Functions

Buổi 7: Python — Lập trình cơ bản, đọc/ghi file

Buổi 8: Pandas & Numpy — Data Cleaning chuyên nghiệp

✅ Buổi 9: EDA — Khám phá dữ liệu, phát hiện insight

→ Buổi 10: Data Visualization — Matplotlib & Seaborn chuyên sâu

Bạn vừa hoàn thành kỹ năng quan trọng nhất của Data Analyst: biến data thô thành insight có giá trị. EDA không chỉ là chạy describe() rồi vẽ biểu đồ — nó là quá trình đặt câu hỏi, khám phá, phát hiện, kết luận. Từ buổi sau, bạn sẽ nâng cấp khả năng visualization lên chuyên nghiệp với Matplotlib & Seaborn — biến insight thành storytelling bằng hình ảnh.


🔗 Tài liệu tham khảo

  1. Pandas Official Documentation — Visualization — Pandas built-in plotting
  2. Seaborn Official Tutorial — Hướng dẫn Seaborn từ cơ bản đến nâng cao
  3. Matplotlib Official Gallery — Thư viện ví dụ Matplotlib
  4. Kaggle — EDA Notebooks — Hàng ngàn EDA notebooks thực tế từ cộng đồng
  5. Towards Data Science — EDA Guide — Hướng dẫn EDA toàn diện
  6. Python for Data Analysis — Wes McKinney, Chapter 9 — EDA & Visualization từ tác giả Pandas