Mục lục
Mục tiêu bài học
Sau bài học, bạn sẽ:
- Biết Pandas là gì, quan hệ với NumPy, và vai trò trong pipeline AI / ML.
- Tạo được
Seriestừ list, dict, ndarray và đọc các attribute cơ bản. - Tạo được
DataFrametừ dict of lists, list of dicts, ndarray. - Dùng
locvàilocđể truy cập theo label hoặc theo vị trí. - Đổi index với
set_index/reset_index. - Chọn, thêm, drop cột; phân biệt khi nào nên dùng Pandas, khi nào NumPy.
Module 5 (bài 34 đến 41) là phần Pandas. Bài 34 mở đầu, đặt nền cho đọc dữ liệu (CSV / JSON / Excel) ở bài 35 và các thao tác clean / group / merge sau đó.
Pandas là gì
Pandas là thư viện xử lý dữ liệu dạng bảng (tabular data) cho Python, công bố lần đầu năm 2008 bởi Wes McKinney. Tên "pandas" xuất phát từ thuật ngữ kinh tế lượng panel data — dữ liệu quan sát theo nhiều đối tượng qua thời gian.
Pandas được dùng gần như mặc định cho:
- Đọc và ghi dữ liệu từ CSV, JSON, Excel, Parquet, SQL.
- Làm sạch dữ liệu: missing value, đổi kiểu, lọc, deduplicate.
- Khám phá dữ liệu (EDA): thống kê, groupby, pivot.
- Chuẩn bị feature trước khi đưa vào scikit-learn, PyTorch, TensorFlow.
Quan hệ với NumPy: Pandas xây trên NumPy — buffer dữ liệu bên dưới phần lớn là numpy.ndarray. So với NumPy, Pandas thêm vào bốn thứ:
- Label-based index: hàng và cột có nhãn (string, datetime, tuple), không chỉ chỉ số nguyên 0, 1, 2, ...
- Mixed dtype: các cột trong cùng một bảng có thể khác kiểu — cột tên là
object(string), cột tuổi làint64, cột ngày làdatetime64[ns]. - Missing data: giá trị thiếu được biểu diễn bằng
NaN(số) hoặcNaT(datetime) hoặcpd.NA, kèm bộ hàm xử lý (fillna,dropna,isna). - Time series: hỗ trợ resampling, rolling window, timezone, lịch nghỉ — không có trong NumPy thuần.
Phiên bản dùng trong bài: Pandas >= 2.0 (release 04/2023, hỗ trợ Python 3.9 đến 3.12). Pandas 2.x bổ sung backend Arrow cho dtype và cải thiện hiệu năng nhưng API cơ bản trong bài giữ nguyên so với 1.x.
Cài đặt và import
Trên máy local:
pip install pandas
Hoặc qua conda:
conda install pandas
Trên Google Colab và Kaggle, Pandas có sẵn — chỉ cần import. Convention chuẩn của cộng đồng:
import pandas as pd
import numpy as np
print(pd.__version__) # ví dụ: 2.2.2
Alias pd được dùng trong tài liệu chính thức của Pandas, scikit-learn, statsmodels — không nên đổi.
Series — 1D có label index
Series là cấu trúc 1 chiều: một mảng giá trị đi kèm một index. Có thể hiểu Series như một numpy.ndarray 1D được dán nhãn cho từng phần tử.
import pandas as pd
s = pd.Series([10, 20, 30])
print(s)
# 0 10
# 1 20
# 2 30
# dtype: int64
Cột bên trái (0, 1, 2) là index mặc định RangeIndex — tương đương chỉ số nguyên. Có thể truyền index tuỳ ý:
s = pd.Series([10, 20, 30], index=["a", "b", "c"], name="score")
print(s)
# a 10
# b 20
# c 30
# Name: score, dtype: int64
Tạo từ dict — key của dict trở thành index:
s = pd.Series({"a": 10, "b": 20, "c": 30})
print(s["a"]) # 10
Attribute của Series
| Attribute | Ý nghĩa |
|---|---|
values | Mảng giá trị thô — numpy.ndarray |
index | Object Index chứa các label |
dtype | Kiểu dữ liệu của các phần tử |
shape | Tuple kích thước, ví dụ (3,) |
size | Số phần tử |
name | Tên của Series (tuỳ chọn) |
s = pd.Series([10, 20, 30], index=["a", "b", "c"], name="score")
print(s.values) # [10 20 30]
print(s.index) # Index(['a', 'b', 'c'], dtype='object')
print(s.dtype) # int64
print(s.shape) # (3,)
print(s.size) # 3
print(s.name) # score
Truy cập phần tử: label vs vị trí
Có hai cách truy cập: theo label (giá trị trong index) hoặc theo vị trí (0, 1, 2, ...). Pandas khuyến nghị dùng loc và iloc để minh thị:
s = pd.Series([10, 20, 30], index=["a", "b", "c"])
# Theo label
print(s.loc["a"]) # 10
# Theo vị trí
print(s.iloc[0]) # 10
print(s.iloc[-1]) # 30 — phần tử cuối
Cú pháp s["a"] và s[0] vẫn dùng được, nhưng dễ nhập nhằng khi index lại là số nguyên — vì vậy code sản xuất nên dùng loc / iloc cho rõ.
DataFrame — 2D row + column
DataFrame là cấu trúc 2 chiều: một bảng có nhãn cho cả hàng (row index) và cột (column labels). Mỗi cột là một Series; các cột có thể khác dtype.
Tạo từ dict of lists
Cách phổ biến nhất — mỗi key là tên cột, mỗi value là danh sách giá trị của cột đó:
import pandas as pd
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 22],
"city": ["Hanoi", "HCMC", "Da Nang"],
})
print(df)
# name age city
# 0 Alice 25 Hanoi
# 1 Bob 30 HCMC
# 2 Carol 22 Da Nang
Tạo từ list of dicts
Mỗi dict là một hàng — key thành tên cột. Tiện khi dữ liệu đến từ JSON / API:
rows = [
{"name": "Alice", "age": 25},
{"name": "Bob", "age": 30},
{"name": "Carol", "age": 22},
]
df = pd.DataFrame(rows)
print(df.shape) # (3, 2)
Tạo từ ndarray
Khi đã có dữ liệu số ở dạng NumPy, có thể bọc thành DataFrame và đặt tên cột:
import numpy as np
arr = np.array([
[1.0, 2.0, 3.0],
[4.0, 5.0, 6.0],
])
df = pd.DataFrame(arr, columns=["x", "y", "z"], index=["r1", "r2"])
print(df)
# x y z
# r1 1.0 2.0 3.0
# r2 4.0 5.0 6.0
Convention đặt tên cột: lowercase, snake_case, tránh space và ký tự đặc biệt — vì cú pháp df.col_name chỉ chạy khi tên cột là Python identifier hợp lệ. Tên "first name" sẽ phải truy cập bằng df["first name"], không dùng được df.first name.
Attributes của DataFrame
| Attribute | Ý nghĩa |
|---|---|
shape | Tuple (rows, columns) |
columns | Object Index chứa tên cột |
index | Object Index chứa nhãn hàng |
dtypes | Series ánh xạ tên cột tới dtype của cột đó |
values | Underlying numpy.ndarray 2D — chỉ ý nghĩa khi mọi cột cùng dtype |
size | Tổng số ô (rows * columns) |
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 22],
})
print(df.shape) # (3, 2)
print(df.columns) # Index(['name', 'age'], dtype='object')
print(df.index) # RangeIndex(start=0, stop=3, step=1)
print(df.dtypes)
# name object
# age int64
# dtype: object
print(df.size) # 6
Cột name có dtype object — đây là cách Pandas lưu cột string (bên dưới là array các con trỏ Python string). Pandas 2.x có thêm dtype string chuyên dụng và backend Arrow, nhưng object vẫn là mặc định.
Lưu ý về .values: khi các cột khác dtype, kết quả sẽ là ndarray dtype object — chậm và mất ưu thế của NumPy. Pandas 1.0+ khuyến nghị dùng .to_numpy() để minh thị, và chỉ gọi khi đã chắc các cột cùng kiểu số.
Series là cột của DataFrame
Truy cập một cột của DataFrame bằng tên — kết quả là một Series chia sẻ cùng index với DataFrame:
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 22],
})
ages = df["age"]
print(type(ages)) # <class 'pandas.core.series.Series'>
print(ages)
# 0 25
# 1 30
# 2 22
# Name: age, dtype: int64
Chọn nhiều cột — truyền list, kết quả là DataFrame:
subset = df[["name", "age"]]
print(type(subset)) # <class 'pandas.core.frame.DataFrame'>
Điểm cần nhớ: df["age"] trả Series, df[["age"]] trả DataFrame một cột. Hai object khác nhau, dù in ra trông tương tự.
Index và set_index / reset_index
Khi tạo DataFrame mà không chỉ định, index là RangeIndex(0, 1, 2, ...). Có thể chuyển một cột thành index để truy vấn theo label cho gọn:
df = pd.DataFrame({
"id": [101, 102, 103],
"name": ["Alice", "Bob", "Carol"],
"age": [25, 30, 22],
})
df2 = df.set_index("id")
print(df2)
# name age
# id
# 101 Alice 25
# 102 Bob 30
# 103 Carol 22
print(df2.loc[101]) # truy cập hàng có id=101
set_index mặc định trả về DataFrame mới, không sửa df gốc. Muốn sửa tại chỗ thì truyền inplace=True (không khuyến nghị vì khó debug) hoặc đơn giản là gán lại.
Đi ngược lại — đưa index về cột thường và quay lại RangeIndex:
df3 = df2.reset_index()
print(df3.columns.tolist()) # ['id', 'name', 'age']
Multi-level index (xem trước)
Index có thể có nhiều cấp — gọi là MultiIndex. Ví dụ ngắn để biết hình thù; chi tiết sẽ ở module groupby:
idx = pd.MultiIndex.from_tuples(
[("HN", 2024), ("HN", 2025), ("HCMC", 2024)],
names=["city", "year"],
)
s = pd.Series([100, 120, 200], index=idx)
print(s.loc[("HN", 2024)]) # 100
MultiIndex xuất hiện tự nhiên sau groupby hai khoá hoặc khi xếp panel data — bài groupby ở cuối module sẽ quay lại.
DataFrame vs NumPy ndarray
Hai cấu trúc nhìn giống nhau (đều bảng số 2D) nhưng vai trò khác hẳn:
| Đặc điểm | numpy.ndarray | pandas.DataFrame |
|---|---|---|
| dtype các cột | Đồng nhất | Hỗn hợp (mỗi cột một dtype) |
| Label hàng / cột | Không | Có |
| Missing value | Phải tự xử lý | NaN / NaT / pd.NA tích hợp |
| Time series | Không | Có (DatetimeIndex, resample, ...) |
| Tốc độ trên array số thuần | Nhanh hơn | Có overhead nhẹ (label, dtype hỗn hợp) |
| Bộ nhớ | Tiết kiệm hơn | Tốn thêm cho index và metadata |
Quy tắc thực tế khi viết pipeline AI:
- Khám phá và preprocess (load, clean, feature engineering, EDA): dùng Pandas — label và mixed dtype giúp code dễ đọc, ít lỗi.
- Train model (gradient descent, ma trận, GPU): chuyển sang NumPy hoặc tensor (PyTorch, TensorFlow) — đồng nhất kiểu, tận dụng SIMD và GPU.
Cầu nối giữa hai bên thường là df.to_numpy() hoặc df.values để lấy ndarray, sau đó torch.from_numpy(...) nếu cần tensor PyTorch.
Operations cơ bản với cột
Bốn thao tác cột xuất hiện ở gần như mọi notebook:
1. Chọn cột
df = pd.DataFrame({
"name": ["Alice", "Bob", "Carol"],
"math": [8.0, 6.5, 9.0],
"lit": [7.0, 7.5, 6.0],
})
print(df["math"]) # Series — 1 cột
print(df[["math", "lit"]]) # DataFrame — nhiều cột
2. Thêm cột mới
Gán cho khoá chưa tồn tại — Pandas vectorize phép tính, không cần loop:
df["avg"] = (df["math"] + df["lit"]) / 2
print(df)
# name math lit avg
# 0 Alice 8.0 7.0 7.50
# 1 Bob 6.5 7.5 7.00
# 2 Carol 9.0 6.0 7.50
3. Cột boolean từ điều kiện
df["passed"] = df["avg"] >= 7.0
print(df["passed"].dtype) # bool
4. Drop cột
drop mặc định trả DataFrame mới, không sửa gốc:
df2 = df.drop(columns=["lit"])
print(df2.columns.tolist()) # ['name', 'math', 'avg', 'passed']
print(df.columns.tolist()) # vẫn còn 'lit' — df gốc không đổi
Dropt nhiều cột cùng lúc: df.drop(columns=["a", "b"]). Để drop hàng, dùng df.drop(index=[...]) hoặc df.drop(labels=[...], axis=0).
Use case AI thường gặp
- Đọc CSV dataset (bài 35) thành DataFrame rồi
df.head()để xem mẫu. - Tạo feature mới từ cột hiện có (
df["bmi"] = df["weight"] / df["height"]**2). groupbyđể tổng hợp theo nhóm (bài 41) — ví dụ doanh thu theo tháng.- Train / val / test split: tách DataFrame theo
train_test_splitrồi mới chuyển sang ndarray cho sklearn / PyTorch.
Bài tập
- Tạo một
Seriesgồm 5 giá trị (ví dụ dân số ước lượng theo triệu), index là tên 5 thành phố tuỳ chọn. Inshape,dtype,index. - Tạo một
DataFramechứa thông tin học sinh với 3 cột:name,age,score; ít nhất 4 hàng. - Thêm cột
passedvới giá trịTruenếuscore >= 5, ngược lạiFalse. - In
shape,columns,dtypescủa DataFrame sau khi đã thêm cộtpassed. - Đổi index của DataFrame sang cột
namebằngset_index, rồi truy cập hàng của một học sinh bằngloc. - Chuyển
df(chỉ các cột sốage,score) sang ndarray bằngto_numpy()và in.shape,.dtypecủa kết quả.
Đáp án
-
import pandas as pd cities = pd.Series( [8.5, 9.2, 1.2, 1.7, 1.1], index=["Hanoi", "HCMC", "Da Nang", "Hai Phong", "Can Tho"], name="population_mil", ) print(cities.shape) # (5,) print(cities.dtype) # float64 print(cities.index) -
df = pd.DataFrame({ "name": ["Alice", "Bob", "Carol", "Dan"], "age": [15, 16, 15, 17], "score": [8.0, 4.5, 6.0, 9.0], }) -
df["passed"] = df["score"] >= 5 # name age score passed # 0 Alice 15 8.0 True # 1 Bob 16 4.5 False # 2 Carol 15 6.0 True # 3 Dan 17 9.0 True -
print(df.shape) # (4, 4) print(df.columns) # Index(['name', 'age', 'score', 'passed'], dtype='object') print(df.dtypes) # name object # age int64 # score float64 # passed bool -
df2 = df.set_index("name") print(df2.loc["Alice"]) # age 15 # score 8.0 # passed True -
arr = df[["age", "score"]].to_numpy() print(arr.shape) # (4, 2) print(arr.dtype) # float64 — bị promote vì có cột float
Tổng kết và bài tiếp theo
- Pandas là thư viện tabular data của Python, xây trên NumPy, thêm label index, mixed dtype, missing data, time series.
Serieslà 1D có label;DataFramelà 2D có row index và column labels. Mỗi cột của DataFrame là một Series.- Tạo từ list, dict of lists, list of dicts hoặc ndarray. Convention đặt tên cột: lowercase snake_case.
- Attribute hay dùng của DataFrame:
shape,columns,index,dtypes,size,values/to_numpy(). - Truy cập theo label dùng
loc, theo vị trí dùngiloc. Đổi index vớiset_index/reset_index. - Pandas cho preprocess / EDA, NumPy / tensor cho training — chuyển qua lại bằng
to_numpy().
Bài 35 đi vào thao tác phổ biến nhất: đọc dữ liệu từ CSV, JSON, Excel thành DataFrame; các tham số quan trọng của pd.read_csv (separator, encoding, parse_dates, dtype); và ghi ngược ra file.
