🎯 Mục tiêu bài học
Sau bài học này, bạn sẽ:
✅ Tạo và khám phá DataFrame từ nhiều nguồn dữ liệu
✅ Chọn, lọc, sắp xếp dữ liệu với loc, iloc và Boolean Indexing
✅ Biến đổi dữ liệu với apply, map, assign
✅ Nhóm và tổng hợp dữ liệu với Groupby
✅ Kết hợp bảng dữ liệu với Merge, Concat
✅ Tạo Pivot Table và chuyển đổi Wide ↔ Long format
Thời gian: 4 giờ | Độ khó: Beginner → Intermediate | Yêu cầu: NumPy cơ bản (Bài 4-5)
📖 Bảng Thuật Ngữ Quan Trọng
| Thuật ngữ | Tiếng Việt | Mô tả |
|---|---|---|
| DataFrame | Khung dữ liệu | Bảng 2D có labels (giống Excel/SQL table) |
| Series | Chuỗi | Mảng 1D có labels — 1 cột của DataFrame |
| Index | Chỉ mục | Nhãn dòng (row labels) |
| loc | Chọn theo nhãn | Truy cập dữ liệu bằng tên cột/index |
| iloc | Chọn theo vị trí | Truy cập bằng số thứ tự (0, 1, 2...) |
| Groupby | Nhóm | Chia nhóm → Tính toán → Gộp kết quả |
| Merge | Kết hợp bảng | Giống SQL JOIN — nối theo key |
| Pivot Table | Bảng tổng hợp | Tổng hợp dữ liệu theo dòng/cột |
| Aggregation | Tổng hợp | Hàm gộp: sum, mean, count, max, min |
| Boolean Indexing | Lọc điều kiện | Lọc dữ liệu bằng mảng True/False |
Checkpoint
Bạn có thể giải thích sự khác biệt giữa loc và iloc không? Khi nào dùng cái nào?
📚 Giới thiệu Pandas
Pandas là gì?
Pandas là thư viện xử lý dữ liệu #1 trong Python. Hãy tưởng tượng Pandas như Excel được nâng cấp bằng code — bạn có thể đọc file, lọc dữ liệu, tính toán, và vẽ biểu đồ, nhưng với triệu dòng thay vì bị giới hạn bởi Excel. Pandas cung cấp:
- DataFrame: Bảng 2D (giống Excel/SQL table)
- Series: Mảng 1D với labels
1import pandas as pd2import numpy as np34print(pd.__version__) # 2.x.xDataFrame vs NumPy Array
| Đặc điểm | NumPy Array | Pandas DataFrame |
|---|---|---|
| Kiểu dữ liệu | Đồng nhất | Hỗn hợp (mixed) |
| Labels | Không | Có (tên cột, index) |
| Missing values | NaN (limited) | NaN, NA (full support) |
| Đọc file | Không | CSV, Excel, SQL, JSON... |
| SQL-like | Không | groupby, merge, pivot |
Polars là thư viện mới hơn, nhanh hơn Pandas cho big data. Tuy nhiên Pandas vẫn là tiêu chuẩn ngành và bắt buộc phải biết. Sau khi thành thạo Pandas, bạn có thể tìm hiểu thêm Polars.
🧱 Tạo DataFrame
Từ Dictionary
1data = {2 "name": ["Alice", "Bob", "Charlie"],3 "age": [25, 30, 35],4 "city": ["Hanoi", "HCMC", "Danang"]5}6df = pd.DataFrame(data)7print(df)8# name age city9# 0 Alice 25 Hanoi10# 1 Bob 30 HCMC11# 2 Charlie 35 DanangTừ List of Dicts
1data = [2 {"name": "Alice", "age": 25},3 {"name": "Bob", "age": 30},4 {"name": "Charlie", "age": 35}5]6df = pd.DataFrame(data)Từ File (Quan trọng nhất!)
1# CSV2df = pd.read_csv("data.csv")3df = pd.read_csv("data.csv", sep=";", encoding="utf-8")45# Excel6df = pd.read_excel("data.xlsx", sheet_name="Sheet1")78# JSON9df = pd.read_json("data.json")1011# SQL12import sqlite313conn = sqlite3.connect("database.db")14df = pd.read_sql("SELECT * FROM users", conn)1516# Parquet (hiệu quả cho big data)17df = pd.read_parquet("data.parquet")Đọc file lớn? Dùng các tham số để tối ưu:
1df = pd.read_csv("big_file.csv",2 usecols=["col1", "col2"], # Chỉ đọc cột cần3 nrows=10000, # Limit dòng4 dtype={"id": "int32"}, # Tiết kiệm RAM5 parse_dates=["date_col"] # Parse ngày tháng6)Checkpoint
Tạo DataFrame từ dict chứa 5 sinh viên (name, score, major). In .shape và .info().
🔍 Khám Phá Dữ Liệu
Xem tổng quan
1df.shape # (rows, columns)2df.head() # 5 dòng đầu3df.head(10) # 10 dòng đầu4df.tail() # 5 dòng cuối5df.sample(5) # 5 dòng random67df.info() # Kiểu dữ liệu, null count8df.describe() # Thống kê mô tả (numeric)9df.dtypes # Kiểu dữ liệu các cột10df.columns # Tên các cột11df.index # IndexUnique và Value Counts
1df['city'].unique() # Array unique values2df['city'].nunique() # Số lượng unique34# Đếm tần suất5df['city'].value_counts()6# Hanoi 1007# HCMC 808# Danang 50910# Với tỷ lệ %11df['city'].value_counts(normalize=True)Workflow khám phá dữ liệu chuẩn:
df.shape→ biết kích thướcdf.head()→ xem dữ liệu mẫudf.info()→ kiểm tra kiểu dữ liệu + nulldf.describe()→ thống kê cơ bảndf[col].value_counts()→ phân phối categorical
Checkpoint
Khi nhận 1 dataset mới, 5 dòng lệnh đầu tiên bạn chạy là gì?
📌 Selection — Chọn Dữ Liệu
Selection là gì? Là cách chọn ra đúng dữ liệu bạn cần từ bảng lớn — giống như câu lệnh SELECT ... WHERE ... trong SQL. Đây là thao tác bạn làm nhiều nhất khi phân tích dữ liệu.
Chọn cột
1# Một cột → Series2df['name']3df.name # Shorthand (tránh nếu trùng method name)45# Nhiều cột → DataFrame6df[['name', 'age']]78# Filter cột9df.filter(like='name') # Cột chứa 'name'10df.filter(regex='^col_') # Cột bắt đầu với 'col_'loc — Chọn theo Label
1df.loc[0] # Dòng có index 02df.loc[0:5] # Dòng 0 đến 5 (INCLUSIVE!)3df.loc[0, 'name'] # Ô [dòng 0, cột 'name']4df.loc[:, 'name':'age'] # Tất cả dòng, cột từ name đến ageiloc — Chọn theo Position
1df.iloc[0] # Dòng đầu tiên2df.iloc[0:5] # Dòng 0 đến 4 (EXCLUSIVE!)3df.iloc[0, 0] # Ô [dòng 0, cột 0]4df.iloc[:, 0:2] # Tất cả dòng, 2 cột đầuloc vs iloc — Điểm khác quan trọng:
df.loc[0:5]→ 6 dòng (0, 1, 2, 3, 4, 5) — inclusivedf.iloc[0:5]→ 5 dòng (0, 1, 2, 3, 4) — exclusive
Giống Python slicing, iloc EXCLUSIVE đầu cuối. Nhưng loc INCLUSIVE cả hai!
Boolean Indexing (Rất quan trọng!)
Boolean Indexing là cách lọc dữ liệu bằng điều kiện. Khi viết df[df['age'] > 30], Python tạo một mảng True/False cho mỗi dòng, rồi chỉ giữ những dòng True. Đây là kỹ thuật bạn sẽ dùng hàng ngày!
1# Lọc đơn giản2df[df['age'] > 30]34# Nhiều điều kiện (dùng & | ~, PHẢI có ngoặc)5df[(df['age'] > 25) & (df['city'] == 'Hanoi')] # AND6df[(df['age'] > 30) | (df['city'] == 'HCMC')] # OR7df[~(df['age'] > 30)] # NOT89# isin — lọc danh sách10df[df['city'].isin(['Hanoi', 'HCMC'])]1112# between — lọc khoảng13df[df['age'].between(25, 35)]1415# String methods16df[df['name'].str.contains('A')]17df[df['name'].str.startswith('A')]Query Method (SQL-like)
1df.query('age > 30')2df.query('age > 25 and city == "Hanoi"')3df.query('city in ["Hanoi", "HCMC"]')45# Dùng biến Python6min_age = 257df.query('age > @min_age')Checkpoint
Cho DataFrame 100 dòng. Lấy ra các dòng có age > 25 VÀ city là "Hanoi" hoặc "HCMC". Viết bằng 2 cách: boolean indexing và query.
🔄 Transformations — Biến Đổi Dữ Liệu
Thêm/Sửa cột
1# Thêm cột mới2df['country'] = 'Vietnam'3df['age_group'] = df['age'].apply(lambda x: 'Young' if x < 30 else 'Adult')45# Tính toán6df['total'] = df['price'] * df['quantity']78# assign — thêm nhiều cột (trả về df mới, không thay đổi gốc)9df = df.assign(10 total = df['price'] * df['quantity'],11 tax = df['price'] * 0.112)Apply và Map
1# apply — áp dụng function lên Series2df['age_squared'] = df['age'].apply(lambda x: x ** 2)34# apply cho cả dòng (axis=1)5def calculate_score(row):6 return row['score1'] + row['score2'] * 0.578df['final_score'] = df.apply(calculate_score, axis=1)910# map — mapping giá trị11city_code = {'Hanoi': 'HN', 'HCMC': 'HCM', 'Danang': 'DN'}12df['city_code'] = df['city'].map(city_code)1314# replace — thay thế giá trị15df['status'] = df['status'].replace({'active': 1, 'inactive': 0})Sắp xếp
1df.sort_values('age') # Tăng dần2df.sort_values('age', ascending=False) # Giảm dần3df.sort_values(['city', 'age'], ascending=[True, False]) # Nhiều cột4df.sort_index() # Theo indexRename
1df.rename(columns={'old_name': 'new_name'})2df.rename(columns=str.lower) # lowercase3df.columns = df.columns.str.replace(' ', '_') # Replace spacesKhi nào dùng apply vs vectorized?
- Vectorized (
df['a'] + df['b']): Nhanh — ưu tiên dùng apply: Chậm hơn nhưng linh hoạt — dùng khi logic phức tạp- Quy tắc: Nếu làm được bằng vectorized → KHÔNG dùng apply
Checkpoint
Tạo cột grade dựa trên score: A (≥90), B (≥80), C (≥70), D (≥60), F (dưới 60). Dùng apply hoặc np.where.
📊 Groupby — Nhóm và Tổng Hợp
Groupby là gì? Giống GROUP BY trong SQL — chia dữ liệu thành nhóm, rồi tính toán cho mỗi nhóm. Ví dụ: tính tổng doanh thu theo thành phố, điểm trung bình theo lớp, số đơn theo ngày. Groupby theo dòng chảy: Split (điều kiện tách) → Apply (tính) → Combine (gộp kết quả).
Basic Groupby
1# Groupby một cột2df.groupby('city')['revenue'].sum()3df.groupby('city')['revenue'].mean()45# Groupby nhiều cột6df.groupby(['city', 'category'])['revenue'].sum()Multiple Aggregations
1# Named aggregation (cách mới, rõ ràng nhất)2df.groupby('city').agg(3 total_revenue = ('revenue', 'sum'),4 avg_revenue = ('revenue', 'mean'),5 max_revenue = ('revenue', 'max'),6 order_count = ('order_id', 'count'),7 unique_products = ('product_id', 'nunique')8)910# Dictionary style11df.groupby('city').agg({12 'revenue': ['sum', 'mean', 'std'],13 'quantity': 'sum',14 'customers': 'count'15})1617# Custom aggregation18df.groupby('city')['revenue'].agg(lambda x: x.max() - x.min())Transform và Filter
1# transform — giữ nguyên shape (broadcast kết quả groupby)2df['city_mean'] = df.groupby('city')['revenue'].transform('mean')34# Tính % so với group5df['pct_of_group'] = df['revenue'] / df.groupby('city')['revenue'].transform('sum')67# filter — lọc groups thỏa điều kiện8df.groupby('city').filter(lambda x: x['revenue'].sum() > 10000)Groupby workflow: Split → Apply → Combine
- Split: Chia DataFrame thành groups theo key
- Apply: Áp dụng function lên mỗi group
- Combine: Gộp kết quả lại
Tương đương SQL: SELECT city, SUM(revenue) FROM df GROUP BY city
Checkpoint
Cho dataset bán hàng. Tính: tổng doanh thu, số đơn hàng, giá trị trung bình đơn hàng — theo từng thành phố. Dùng .agg().
🔗 Merge, Join và Concat
Merge (như SQL JOIN)
1orders = pd.DataFrame({2 'order_id': [1, 2, 3],3 'customer_id': [101, 102, 101],4 'amount': [100, 200, 150]5})6customers = pd.DataFrame({7 'customer_id': [101, 102, 103],8 'name': ['Alice', 'Bob', 'Charlie']9})1011# Inner join (default)12pd.merge(orders, customers, on='customer_id')1314# Left join — giữ tất cả từ bảng trái15pd.merge(orders, customers, on='customer_id', how='left')1617# Right join — giữ tất cả từ bảng phải18pd.merge(orders, customers, on='customer_id', how='right')1920# Outer join — giữ tất cả21pd.merge(orders, customers, on='customer_id', how='outer')2223# Khác tên cột24pd.merge(orders, customers, left_on='cust_id', right_on='customer_id')Concat — Nối DataFrame
1# Nối dọc (thêm dòng) — giống SQL UNION2df_all = pd.concat([df1, df2, df3], ignore_index=True)34# Nối ngang (thêm cột)5df_wide = pd.concat([df1, df2], axis=1)Merge pitfalls:
- Duplicate keys → tạo Cartesian product (dòng nhiều hơn dự kiến!)
- Luôn kiểm tra
.shapetrước và sau merge - Dùng
validate='one_to_many'để catch lỗi sớm:
1pd.merge(orders, customers, on='customer_id', validate='many_to_one')Checkpoint
Merge 2 bảng: orders (order_id, product_id, qty) + products (product_id, name, price). Tính total = qty × price.
📐 Pivot Table và Melt
Pivot Table — Bảng tổng hợp
1pivot = df.pivot_table(2 values='revenue', # Giá trị tính toán3 index='city', # Dòng4 columns='month', # Cột5 aggfunc='sum', # Hàm tổng hợp6 fill_value=0 # Thay NaN7)89# Nhiều aggregations10pivot = df.pivot_table(11 values='revenue',12 index='city',13 columns='month',14 aggfunc=['sum', 'mean', 'count']15)Melt — Wide → Long format
1# Chuyển từ wide format sang long format2df_long = df.melt(3 id_vars=['name', 'city'], # Cột giữ nguyên4 value_vars=['jan', 'feb', 'mar'], # Cột "unpivot"5 var_name='month', # Tên cột mới chứa tên cột cũ6 value_name='revenue' # Tên cột mới chứa giá trị7)Khi nào dùng Pivot vs Melt?
- Pivot: Long → Wide (tóm tắt báo cáo, dashboards)
- Melt: Wide → Long (chuẩn tidy data cho phân tích/visualization)
- Quy tắc tidy data: Mỗi biến là 1 cột, mỗi quan sát là 1 dòng
Checkpoint
Tạo pivot table: doanh thu theo city (dòng) × month (cột). Thêm tổng hàng và tổng cột bằng margins=True.
💾 Export Dữ Liệu
1# CSV2df.to_csv("output.csv", index=False)3df.to_csv("output.csv", index=False, encoding="utf-8-sig") # Excel-friendly45# Excel6df.to_excel("output.xlsx", sheet_name="Data", index=False)78# JSON9df.to_json("output.json", orient="records")1011# Parquet (hiệu quả cho big data)12df.to_parquet("output.parquet")Parquet nhanh hơn CSV 5-10x và nhỏ hơn 3-5x. Ưu tiên dùng cho file > 100MB.
📝 Tổng Kết
Tóm tắt kiến thức
| Chủ đề | Hàm/Method chính |
|---|---|
| Tạo DF | pd.DataFrame(), read_csv(), read_excel(), read_sql() |
| Khám phá | shape, head(), info(), describe(), value_counts() |
| Selection | loc[], iloc[], boolean indexing, query() |
| Transform | apply(), map(), assign(), sort_values(), rename() |
| Groupby | groupby().agg(), transform(), filter() |
| Merge | pd.merge(), pd.concat() |
| Pivot | pivot_table(), melt() |
| Export | to_csv(), to_excel(), to_parquet() |
Quick Reference
1import pandas as pd23# Read4df = pd.read_csv("data.csv")56# Explore7df.shape, df.info(), df.describe(), df.head()89# Select10df[df['col'] > 10]11df.loc[0:5, 'name':'age']12df.query('age > 25')1314# Transform15df['new'] = df['a'] + df['b']16df.groupby('city')['revenue'].agg(['sum', 'mean'])1718# Merge19pd.merge(df1, df2, on='key', how='left')2021# Export22df.to_csv("out.csv", index=False)Bài tiếp theo: Thực hành Pandas với dataset thực tế — phân tích dữ liệu bán hàng! 📊
Câu hỏi tự kiểm tra
loc[]vàiloc[]khác nhau như thế nào khi truy cập dữ liệu trong DataFrame?- GroupBy trong Pandas hoạt động theo mô hình nào? Cho ví dụ tính tổng doanh thu theo thành phố.
- Sự khác biệt giữa
merge()vớihow='inner'vàhow='left'là gì? - Pivot table và melt dùng trong trường hợp nào? Khi nào chuyển từ long sang wide format?
🎉 Tuyệt vời! Bạn đã hoàn thành bài học Pandas!
Tiếp theo: Thực hành Pandas với dataset thực tế để nắm vững kỹ năng xử lý dữ liệu dạng bảng!
