Skip to content

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

Pandas = Excel on steroids. Xử lý 1 triệu dòng trong vài giây — điều mà Excel chỉ biết treo máy.

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

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

  1. Tạo và thao tác DataFrame: select, filter, sort, group
  2. Xử lý missing values: dropna, fillna, interpolate
  3. Xử lý duplicates, data type conversion, string cleaning
  4. Merge/concat DataFrames (tương đương JOIN trong SQL)

📋 Tổng quan

Ở Buổi 7, bạn đã nắm vững Python cơ bản — data types (int, float, str, list, dict), control flow (if/else, for, while), viết function, và đọc/ghi file. Bạn biết Python là ngôn ngữ linh hoạt, xử lý được mọi format dữ liệu. Nhưng nếu chỉ dùng Python thuần để phân tích data — bạn sẽ viết hàng trăm dòng code chỉ để lọc, nhóm, tính tổng. Đó là lý do Pandas tồn tại.

Pandas là thư viện Python chuyên xử lý dữ liệu dạng bảng (tabular data) — chính xác thứ mà Data Analyst làm việc hàng ngày. Hãy nghĩ Pandas như sự kết hợp hoàn hảo: nó có giao diện bảng như Excel, sức mạnh truy vấn như SQL, và tốc độ xử lý của Python. Còn Numpy (Numerical Python) là nền tảng toán học bên dưới Pandas — mỗi cột DataFrame thực chất là một numpy array, và hiểu Numpy giúp bạn viết code nhanh hơn gấp 10–100 lần so với Python thuần.

Nhớ lại hành trình OSEMN (Obtain → Scrub → Explore → Model → iNterpret): Buổi 7 bạn đã biết cách Obtain dữ liệu bằng Python (open, csv, json). Buổi 8 tập trung vào bước Scrub — làm sạch dữ liệu bằng Pandas & Numpy, bước quan trọng nhất chiếm 60–80% thời gian của Data Analyst.

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["🤖 Model<br/>Buổi 12+"]
    D --> E["📊 iNterpret<br/>Buổi 10-11: Visualization"]
    style B fill:#e8f5e9,stroke:#4caf50,stroke-width:3px

💡 Tại sao chuyển từ Excel sang Pandas?

Tiêu chíExcelPandas
Giới hạn dòng~1,048,576 dòngHàng chục triệu dòng (tuỳ RAM)
Tốc độ xử lý 1M dòngTreo máy / crash2–5 giây
Lặp lại thao tácClick lại từ đầu1 dòng code, chạy lại vô hạn
Audit trailKhông biết ai sửa gìCode = documentation
Kết nối data sourcesLimitedCSV, Excel, SQL, API, JSON, Parquet...

📌 Phần 1: Pandas Basics — DataFrame là gì và tại sao nó mạnh

Series vs DataFrame

Pandas có 2 cấu trúc dữ liệu chính:

  • Series: Một cột dữ liệu — giống 1 cột trong Excel hoặc 1 list Python, nhưng có index (nhãn cho mỗi row).
  • DataFrame: Bảng 2 chiều — gồm nhiều Series ghép lại, giống 1 sheet Excel hoặc 1 bảng SQL.
python
import pandas as pd
import numpy as np

# Series — 1 cột doanh thu
revenue = pd.Series([15000000, 22000000, 18500000, 31000000],
                    index=["Q1", "Q2", "Q3", "Q4"],
                    name="doanh_thu")
print(revenue)
# Q1    15000000
# Q2    22000000
# Q3    18500000
# Q4    31000000
# Name: doanh_thu, dtype: int64
python
# DataFrame — bảng dữ liệu hoàn chỉnh
df = pd.DataFrame({
    "san_pham":   ["Laptop", "Chuột", "Bàn phím", "Màn hình", "Tai nghe"],
    "gia":        [25000000, 350000, 1200000, 8500000, 2500000],
    "so_luong":   [120, 500, 300, 80, 450],
    "danh_muc":   ["Máy tính", "Phụ kiện", "Phụ kiện", "Máy tính", "Phụ kiện"]
})
print(df)
san_phamgiaso_luongdanh_muc
0Laptop25000000120Máy tính
1Chuột350000500Phụ kiện
2Bàn phím1200000300Phụ kiện
3Màn hình850000080Máy tính
4Tai nghe2500000450Phụ kiện

Đọc dữ liệu từ file

Trong thực tế, bạn hiếm khi tự gõ data. Pandas đọc được gần như mọi format:

python
# Đọc CSV — format phổ biến nhất
df = pd.read_csv("sales_data.csv")

# Đọc Excel — khi đồng nghiệp gửi file .xlsx
df = pd.read_excel("bao_cao_q4.xlsx", sheet_name="Sheet1")

# Đọc JSON — khi nhận data từ API
df = pd.read_json("api_response.json")

# Đọc từ SQL — kết nối trực tiếp database
import sqlite3
conn = sqlite3.connect("ecommerce.db")
df = pd.read_sql("SELECT * FROM orders WHERE year = 2025", conn)
python
# Kiểm tra nhanh dataset sau khi đọc — 5 lệnh "must-run"
print(f"Kích thước: {df.shape[0]:,} dòng × {df.shape[1]} cột")
print(df.head())          # 5 dòng đầu
print(df.info())          # Kiểu dữ liệu, missing values
print(df.describe())      # Thống kê cơ bản (mean, std, min, max)
print(df.dtypes)          # Kiểu dữ liệu từng cột

Selection — Chọn dữ liệu: loc, iloc, boolean indexing

Đây là kỹ năng quan trọng nhất — tương đương SELECT ... WHERE trong SQL và Filter trong Excel.

python
# loc — chọn theo TÊN cột/index (label-based)
df.loc[0:2, ["san_pham", "gia"]]       # Dòng 0-2, cột san_pham & gia

# iloc — chọn theo VỊ TRÍ (integer-based)
df.iloc[0:3, [0, 1]]                    # 3 dòng đầu, 2 cột đầu

# Boolean indexing — lọc theo điều kiện (= WHERE trong SQL)
expensive = df[df["gia"] > 2000000]     # Sản phẩm giá > 2 triệu
print(expensive)

💡 So sánh 3 công cụ — Lọc dữ liệu

Thao tácExcelSQLPandas
Lọc giá > 2MFilter → Number > 2000000WHERE gia > 2000000df[df["gia"] > 2000000]
Chọn 2 cộtẨn cột khácSELECT col1, col2df[["col1", "col2"]]
Lọc nhiều điều kiệnFilter nhiều lầnWHERE a > 1 AND b = 'X'df[(df["a"] > 1) & (df["b"] == "X")]

Thao tác cột — rename, drop, assign, apply

python
# Thêm cột mới — tính doanh thu
df["doanh_thu"] = df["gia"] * df["so_luong"]

# Đổi tên cột
df = df.rename(columns={"gia": "don_gia", "so_luong": "sl_ban"})

# Xóa cột không cần
df = df.drop(columns=["danh_muc"])

# apply — áp dụng function lên từng giá trị (giống kéo công thức Excel)
df["phan_loai_gia"] = df["don_gia"].apply(
    lambda x: "Cao cấp" if x > 5000000 else "Phổ thông"
)
print(df[["san_pham", "don_gia", "phan_loai_gia"]])
san_phamdon_giaphan_loai_gia
0Laptop25000000Cao cấp
1Chuột350000Phổ thông
2Bàn phím1200000Phổ thông
3Màn hình8500000Cao cấp
4Tai nghe2500000Phổ thông

📌 Phần 2: Data Cleaning với Pandas — Nghệ thuật làm sạch dữ liệu

Data thực tế luôn bẩn — missing values, duplicate rows, sai kiểu dữ liệu, chuỗi không nhất quán. Bước Scrub trong OSEMN chiếm 60–80% thời gian, và Pandas là vũ khí tối thượng cho việc này.

Missing values — Xử lý giá trị thiếu (NaN)

python
# Tạo dataset có missing values (NaN) — mô phỏng thực tế
df_dirty = pd.DataFrame({
    "ma_kh":     ["KH001", "KH002", "KH003", "KH004", "KH005", "KH006"],
    "ten":       ["An", "Bình", None, "Dũng", "Em", "Phúc"],
    "email":     ["an@mail.com", None, "cuong@mail.com", None, "em@mail.com", "phuc@mail.com"],
    "tuoi":      [28, 35, None, 42, 31, None],
    "doanh_thu": [5000000, 12000000, 8000000, None, 3500000, 9500000]
})
print(df_dirty)
ma_khtenemailtuoidoanh_thu
0KH001Anan@mail.com28.05000000.0
1KH002BìnhNaN35.012000000.0
2KH003NaNcuong@mail.comNaN8000000.0
3KH004DũngNaN42.0NaN
4KH005Emem@mail.com31.03500000.0
5KH006Phúcphuc@mail.comNaN9500000.0
python
# Bước 1: Kiểm tra missing values
print(df_dirty.isnull().sum())
# ma_kh        0
# ten          1
# email        2
# tuoi         2
# doanh_thu    1

print(f"Tỷ lệ missing: {df_dirty.isnull().sum().sum() / df_dirty.size * 100:.1f}%")
# Tỷ lệ missing: 20.0%
python
# Bước 2: Xử lý — chọn chiến lược phù hợp từng cột

# dropna — xóa dòng có NaN (khi missing ít, < 5%)
df_clean = df_dirty.dropna(subset=["ten"])  # Xóa dòng thiếu tên

# fillna — điền giá trị thay thế
df_dirty["tuoi"] = df_dirty["tuoi"].fillna(df_dirty["tuoi"].median())       # Điền median
df_dirty["doanh_thu"] = df_dirty["doanh_thu"].fillna(df_dirty["doanh_thu"].mean())  # Điền mean
df_dirty["email"] = df_dirty["email"].fillna("không_có@unknown.com")         # Điền giá trị mặc định

# ffill / bfill — điền theo dòng trước/sau (time series)
# df["price"] = df["price"].fillna(method="ffill")  # Forward fill

⚠️ Lỗi phổ biến khi xử lý NaN

  1. Xóa hết NaN mà không suy nghĩ → mất 50% data. Luôn kiểm tra tỷ lệ missing trước!
  2. Điền mean cho cột có outlier → bị lệch. Dùng median thay vì mean khi dữ liệu skewed.
  3. Quên kiểm tra cột nào bị NaN → Luôn chạy df.isnull().sum() đầu tiên.

Duplicates — Xử lý dòng trùng lặp

python
# Tạo data có duplicate
df_orders = pd.DataFrame({
    "order_id":  [1001, 1002, 1002, 1003, 1004, 1004],
    "san_pham":  ["Laptop", "Chuột", "Chuột", "Bàn phím", "Tai nghe", "Tai nghe"],
    "gia":       [25000000, 350000, 350000, 1200000, 2500000, 2500000]
})

# Kiểm tra duplicate
print(df_orders.duplicated().sum())        # 2 dòng trùng
print(df_orders[df_orders.duplicated()])   # Xem dòng nào trùng

# Xóa duplicate — giữ dòng đầu tiên
df_orders = df_orders.drop_duplicates(subset=["order_id"], keep="first")
print(f"Sau cleaning: {len(df_orders)} dòng")
# Sau cleaning: 4 dòng

Data type conversion — Chuyển đổi kiểu dữ liệu

python
# Thực tế: cột ngày thường bị đọc thành string
df_sales = pd.DataFrame({
    "ngay_mua":   ["2025-01-15", "2025-02-20", "2025-03-10"],
    "gia_str":    ["15,000,000", "22,500,000", "8,750,000"],
    "so_luong":   ["5", "3", "10"]
})

# Chuyển string → datetime
df_sales["ngay_mua"] = pd.to_datetime(df_sales["ngay_mua"])

# Chuyển string có dấu phẩy → số
df_sales["gia"] = df_sales["gia_str"].str.replace(",", "").astype(int)

# Chuyển string → int
df_sales["so_luong"] = pd.to_numeric(df_sales["so_luong"])

print(df_sales.dtypes)
# ngay_mua     datetime64[ns]
# gia_str      object
# gia          int64
# so_luong     int64

String cleaning — Làm sạch chuỗi

python
# Data thực tế: chuỗi lộn xộn, không nhất quán
df_customers = pd.DataFrame({
    "ten":       ["  Nguyễn Văn An  ", "TRẦN THỊ BÌNH", "lê văn cường", "Phạm Dũng "],
    "email":     ["AN@Gmail.COM", "binh@yahoo.com", "CUONG@MAIL.COM", "dung@mail.com"],
    "thanh_pho": ["Hà Nội", "hà nội", "HÀ NỘI", "TP.HCM"]
})

# Chuẩn hóa chuỗi bằng .str accessor
df_customers["ten"] = df_customers["ten"].str.strip().str.title()
df_customers["email"] = df_customers["email"].str.lower().str.strip()
df_customers["thanh_pho"] = df_customers["thanh_pho"].str.title()

# Tìm kiếm & thay thế
has_gmail = df_customers["email"].str.contains("gmail")
df_customers["email_provider"] = df_customers["email"].str.extract(r"@(\w+)\.")

print(df_customers)
tenemailthanh_phoemail_provider
0Nguyễn Văn Anan@gmail.comHà Nộigmail
1Trần Thị Bìnhbinh@yahoo.comHà Nộiyahoo
2Lê Văn Cườngcuong@mail.comHà Nộimail
3Phạm Dũngdung@mail.comTp.Hcmmail

📌 Method Chaining — Viết code Pandas "sạch"

Thay vì viết từng dòng riêng lẻ, Pandas cho phép nối các thao tác thành pipeline:

python
# ❌ Không tối ưu — nhiều dòng trung gian
df = df.dropna(subset=["ten"])
df = df.drop_duplicates()
df = df.reset_index(drop=True)
df = df.sort_values("doanh_thu", ascending=False)

# ✅ Method chaining — sạch, rõ ràng, professional
df_clean = (df
    .dropna(subset=["ten"])
    .drop_duplicates()
    .reset_index(drop=True)
    .sort_values("doanh_thu", ascending=False)
)

Method chaining giống pipeline trong shell (cat file | grep error | sort). Mỗi bước nhận DataFrame từ bước trước, xử lý, trả về DataFrame mới.


📌 Phần 3: Numpy Essentials — Tốc độ và toán học

Numpy array vs Python list — Tại sao nhanh hơn?

Numpy (Numerical Python) là thư viện nền tảng mà Pandas xây dựng trên đó. Numpy array lưu dữ liệu trong bộ nhớ liên tục (contiguous memory) và sử dụng C code bên dưới — nhanh hơn Python list 10–100 lần.

python
import numpy as np
import time

# So sánh tốc độ: Python list vs Numpy array
size = 1_000_000

# Python list
py_list = list(range(size))
start = time.time()
result_py = [x * 2 for x in py_list]
print(f"Python list: {time.time() - start:.4f}s")
# Python list: 0.0850s

# Numpy array
np_arr = np.arange(size)
start = time.time()
result_np = np_arr * 2
print(f"Numpy array: {time.time() - start:.4f}s")
# Numpy array: 0.0015s → Nhanh hơn ~56 lần!

💡 Tại sao Numpy nhanh hơn?

Đặc điểmPython listNumpy array
Kiểu dữ liệuHỗn hợp (int, str, ...)Đồng nhất (toàn int64 hoặc float64)
Bộ nhớPhân tán (pointers)Liên tục (contiguous block)
Vòng lặpPython loop (chậm)Vectorized C code (nhanh)
DataFrameMỗi cột Pandas = 1 numpy array

Vectorized operations — Tính toán không cần loop

python
# Doanh thu và chi phí của 5 cửa hàng (đơn vị: triệu VNĐ)
revenue = np.array([150, 220, 180, 310, 250])
cost    = np.array([90, 130, 110, 180, 160])

# Vectorized — không cần for loop!
profit = revenue - cost
margin = (profit / revenue) * 100

print("Lợi nhuận:", profit)      # [60  90  70 130  90]
print("Biên LN (%):", margin.round(1))  # [40.  40.9 38.9 41.9 36. ]

Statistical functions — Thống kê nhanh

python
daily_sales = np.array([
    15_500_000, 22_300_000, 18_700_000, 31_200_000, 12_800_000,
    28_900_000, 19_500_000, 25_600_000, 14_200_000, 33_100_000
])

print(f"Trung bình:   {np.mean(daily_sales):>15,.0f} VNĐ")
print(f"Trung vị:     {np.median(daily_sales):>15,.0f} VNĐ")
print(f"Độ lệch chuẩn:{np.std(daily_sales):>15,.0f} VNĐ")
print(f"Min:          {np.min(daily_sales):>15,.0f} VNĐ")
print(f"Max:          {np.max(daily_sales):>15,.0f} VNĐ")
print(f"Percentile 25:{np.percentile(daily_sales, 25):>15,.0f} VNĐ")
print(f"Percentile 75:{np.percentile(daily_sales, 75):>15,.0f} VNĐ")

# Trung bình:      22,180,000 VNĐ
# Trung vị:        20,900,000 VNĐ
# Độ lệch chuẩn:    6,864,267 VNĐ
# Min:             12,800,000 VNĐ
# Max:             33,100,000 VNĐ
# Percentile 25:   15,800,000 VNĐ
# Percentile 75:   27,675,000 VNĐ

Boolean masking & np.where

python
# Boolean masking — lọc dữ liệu siêu nhanh
sales = np.array([15, 22, 8, 31, 12, 28, 5, 33, 19, 27])

# Tìm ngày có doanh thu > 20 triệu
high_days = sales[sales > 20]
print(f"Ngày doanh thu cao: {high_days}")      # [22 31 28 33 27]
print(f"Số ngày cao: {len(high_days)}/{len(sales)}")  # 5/10

# np.where — giống IF trong Excel
labels = np.where(sales > 20, "Tốt", "Cần cải thiện")
print(labels)
# ['Cần cải thiện' 'Tốt' 'Cần cải thiện' 'Tốt' 'Cần cải thiện'
#  'Tốt' 'Cần cải thiện' 'Tốt' 'Cần cải thiện' 'Tốt']

📌 Khi nào dùng Numpy vs Pandas?

  • Numpy: Tính toán số học thuần túy, xử lý mảng đồng nhất, cần tốc độ tối đa
  • Pandas: Dữ liệu dạng bảng (nhiều cột, nhiều kiểu), lọc/nhóm/merge, phân tích business
  • Thực tế: Bạn sẽ dùng Pandas 90% thời gian — Numpy chạy "ngầm" bên dưới. Nhưng hiểu Numpy giúp bạn debug nhanh hơn và viết code hiệu quả hơn.

📌 Phần 4: Merge & Concat — Nối bảng như SQL JOIN

Trong thực tế, dữ liệu không bao giờ nằm trong 1 bảng duy nhất. Bạn có bảng orders, bảng customers, bảng products — cần nối chúng lại để phân tích. Nếu bạn biết JOIN trong SQL (Buổi 5), đây chỉ là cú pháp khác cho cùng một tư duy.

pd.merge — SQL JOIN trong Pandas

python
# Bảng đơn hàng
orders = pd.DataFrame({
    "order_id":    [1001, 1002, 1003, 1004, 1005],
    "customer_id": ["KH01", "KH02", "KH03", "KH01", "KH05"],
    "product_id":  ["SP01", "SP02", "SP01", "SP03", "SP02"],
    "so_luong":    [2, 1, 3, 1, 2]
})

# Bảng khách hàng
customers = pd.DataFrame({
    "customer_id": ["KH01", "KH02", "KH03", "KH04"],
    "ten_kh":      ["Nguyễn An", "Trần Bình", "Lê Cường", "Phạm Dũng"],
    "thanh_pho":   ["Hà Nội", "TP.HCM", "Đà Nẵng", "Hà Nội"]
})

# Bảng sản phẩm
products = pd.DataFrame({
    "product_id": ["SP01", "SP02", "SP03"],
    "ten_sp":     ["Laptop", "Chuột Logitech", "Bàn phím cơ"],
    "don_gia":    [25000000, 850000, 2200000]
})
python
# INNER JOIN — chỉ lấy dòng có match ở cả 2 bảng
df_inner = pd.merge(orders, customers, on="customer_id", how="inner")
print(f"Inner join: {len(df_inner)} dòng")  # 4 dòng (KH05 không có trong customers)

# LEFT JOIN — giữ tất cả orders, thêm info customers
df_left = pd.merge(orders, customers, on="customer_id", how="left")
print(f"Left join: {len(df_left)} dòng")    # 5 dòng (KH05 có NaN ở ten_kh, thanh_pho)

# Nối 3 bảng — pipeline thực tế
df_full = (orders
    .merge(customers, on="customer_id", how="left")
    .merge(products, on="product_id", how="left")
)
df_full["thanh_tien"] = df_full["don_gia"] * df_full["so_luong"]
print(df_full[["order_id", "ten_kh", "ten_sp", "thanh_tien"]])
order_idten_khten_spthanh_tien
01001Nguyễn AnLaptop50000000
11002Trần BìnhChuột Logitech850000
21003Lê CườngLaptop75000000
31004Nguyễn AnBàn phím cơ2200000
41005NaNChuột Logitech1700000

💡 So sánh Merge types — Pandas vs SQL

PandasSQLKết quả
how="inner"INNER JOINChỉ dòng match cả 2 bảng
how="left"LEFT JOINGiữ tất cả bảng trái
how="right"RIGHT JOINGiữ tất cả bảng phải
how="outer"FULL OUTER JOINGiữ tất cả cả 2 bảng
on="col"ON a.col = b.colCột dùng để nối

pd.concat — Nối DataFrame theo dòng hoặc cột

concat khác merge: nó ghép DataFrames lại (stack) thay vì nối theo key. Giống UNION trong SQL hơn là JOIN.

python
# Doanh thu Q1 và Q2 — 2 file riêng biệt
q1 = pd.DataFrame({
    "thang": ["T1", "T2", "T3"],
    "doanh_thu": [150_000_000, 180_000_000, 200_000_000]
})
q2 = pd.DataFrame({
    "thang": ["T4", "T5", "T6"],
    "doanh_thu": [220_000_000, 195_000_000, 280_000_000]
})

# Nối theo dòng (axis=0) — ghép Q1 + Q2
half_year = pd.concat([q1, q2], ignore_index=True)
print(half_year)
thangdoanh_thu
0T1150000000
1T2180000000
2T3200000000
3T4220000000
4T5195000000
5T6280000000
python
# Nối theo cột (axis=1) — thêm cột từ nguồn khác
chi_phi = pd.DataFrame({
    "thang": ["T1", "T2", "T3"],
    "chi_phi": [90_000_000, 110_000_000, 120_000_000]
})
q1_full = pd.concat([q1, chi_phi["chi_phi"]], axis=1)
q1_full["loi_nhuan"] = q1_full["doanh_thu"] - q1_full["chi_phi"]
print(q1_full)

Multi-index basics

Khi dữ liệu có nhiều cấp nhóm (ví dụ: doanh thu theo thành phố VÀ theo tháng), Pandas dùng Multi-index để tổ chức:

python
df_sales = pd.DataFrame({
    "thanh_pho": ["Hà Nội", "Hà Nội", "TP.HCM", "TP.HCM", "Đà Nẵng", "Đà Nẵng"],
    "quy":       ["Q1", "Q2", "Q1", "Q2", "Q1", "Q2"],
    "doanh_thu": [500, 620, 780, 850, 300, 340]
})

# Tạo Multi-index
df_multi = df_sales.set_index(["thanh_pho", "quy"])
print(df_multi)

# Truy vấn theo cấp
print(df_multi.loc["Hà Nội"])           # Tất cả quý của Hà Nội
print(df_multi.loc[("TP.HCM", "Q2")])  # TP.HCM quý 2

⚠️ SettingWithCopyWarning — Lỗi kinh điển

python
# ❌ SAI — tạo view, không phải copy → warning
df_filtered = df[df["gia"] > 5000000]
df_filtered["status"] = "premium"   # ⚠️ SettingWithCopyWarning!

# ✅ ĐÚNG — dùng .copy() để tạo bản sao độc lập
df_filtered = df[df["gia"] > 5000000].copy()
df_filtered["status"] = "premium"   # ✅ Không warning

Luôn dùng .copy() khi muốn tạo DataFrame mới từ filter để tránh lỗi này.


🔑 Từ khóa chính

Tiếng ViệtEnglishGiải thích
Khung dữ liệuDataFrameBảng dữ liệu 2 chiều trong Pandas — tương đương 1 sheet Excel
Giá trị thiếuMissing Value (NaN)Ô dữ liệu trống, không có giá trị — cần dropna hoặc fillna
Nối bảngMerge / ConcatGhép 2+ DataFrame — merge = JOIN, concat = UNION
Áp dụng hàmApplyÁp dụng function lên từng dòng/cột — giống kéo công thức Excel
Chỉ mụcIndexNhãn xác định mỗi dòng trong DataFrame — giống Primary Key
Mảng số họcNumpy ArrayCấu trúc dữ liệu cốt lõi của Numpy — nhanh hơn list 10–100x
Phép toán vectorVectorized OperationTính toán trên toàn mảng mà không cần vòng lặp
Dòng trùng lặpDuplicateDòng dữ liệu bị lặp — dùng drop_duplicates để xóa

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

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

  • [ ] Tạo DataFrame, đọc data từ CSV/Excel/JSON/SQL
  • [ ] Chọn dữ liệu bằng loc, iloc, boolean indexing
  • [ ] Thao tác cột: rename, drop, assign, apply
  • [ ] Kiểm tra và xử lý missing values: isnull, dropna, fillna
  • [ ] Xóa duplicates với drop_duplicates
  • [ ] Chuyển đổi kiểu dữ liệu: astype, to_datetime, pd.to_numeric
  • [ ] Làm sạch chuỗi: .str.strip(), .str.lower(), .str.contains()
  • [ ] Dùng Numpy cho tính toán nhanh: np.mean, np.where, boolean masking
  • [ ] Merge DataFrames (inner, left, right, outer) — tương đương SQL JOIN
  • [ ] Concat DataFrames theo dòng/cột
  • [ ] Viết method chaining pipeline sạch

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

Buổi 3-4: Excel — Click chuột, giới hạn 1M dòng

Buổi 5-6: SQL — Query database, JOIN bảng

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 với Python — Phân tích khám phá dữ liệu

Bạn đã hoàn thành bước nhảy lớn nhất: từ click chuột trong Excel → viết code Pandas xử lý hàng triệu dòng. Từ buổi này, bạn chính thức là Pandas user — mọi bài toán data cleaning, dù 100 dòng hay 10 triệu dòng, đều giải quyết bằng cùng một đoạn code.


🔗 Tài liệu tham khảo

  1. Pandas Official Documentation — Tài liệu chính thức, đầy đủ nhất
  2. Numpy Official Documentation — Tài liệu chính thức Numpy
  3. 10 Minutes to Pandas — Hướng dẫn nhanh từ Pandas chính thức
  4. Pandas Cheat Sheet — DataCamp — Bảng tóm tắt lệnh Pandas
  5. Python for Data Analysis — Wes McKinney — Sách kinh điển từ tác giả Pandas
  6. Real Python — Pandas Tutorials — Hướng dẫn thực hành Pandas