Pandas: xử lý dữ liệu dạng bảng trong Python

Pandas là thư viện xử lý dữ liệu dạng bảng được dùng nhiều nhất trong Python. Nếu bạn từng làm việc với Excel hay SQL, bạn sẽ thấy Pandas quen thuộc — nhưng mạnh hơn nhiều khi dữ liệu lớn hoặc thao tác phức tạp.

Hai kiểu dữ liệu cốt lõi

Series là một cột dữ liệu có nhãn. DataFrame là một bảng gồm nhiều cột.

import pandas as pd

df = pd.DataFrame({
    "ten": ["An", "Bình", "Cường", "Dung"],
    "tuoi": [25, 32, 28, 41],
    "phong_ban": ["IT", "HR", "IT", "Kế toán"],
    "luong": [15_000_000, 12_000_000, 18_000_000, 20_000_000],
})

print(df.head())
print(df.info())
print(df.describe())

Ba lệnh head, infodescribe nên là việc đầu tiên bạn làm với mọi tập dữ liệu mới. Chúng cho biết dữ liệu trông ra sao, kiểu của từng cột, và có bao nhiêu giá trị bị thiếu.

Đọc dữ liệu từ file

df = pd.read_csv("du_lieu.csv")
df = pd.read_excel("bao_cao.xlsx", sheet_name="Quý 1")
df = pd.read_json("du_lieu.json")
df = pd.read_sql("SELECT * FROM don_hang", con=engine)

Với file CSV tiếng Việt, hãy chỉ rõ bảng mã để tránh lỗi ký tự:

df = pd.read_csv("du_lieu.csv", encoding="utf-8")
# File xuất từ Excel bản tiếng Việt thường dùng:
df = pd.read_csv("du_lieu.csv", encoding="utf-8-sig")

Chọn và lọc dữ liệu

# Chọn cột
df["ten"]
df[["ten", "luong"]]

# Lọc theo điều kiện
df[df["tuoi"] > 30]
df[(df["phong_ban"] == "IT") & (df["luong"] > 16_000_000)]

# Lọc theo danh sách giá trị
df[df["phong_ban"].isin(["IT", "HR"])]

# Chọn theo nhãn và vị trí
df.loc[0, "ten"]        # theo nhãn
df.iloc[0, 1]           # theo vị trí số

Lưu ý dùng &| thay vì and/or khi kết hợp điều kiện, và luôn bọc mỗi điều kiện trong ngoặc đơn. Đây là lỗi cú pháp mà ai mới dùng Pandas cũng gặp ít nhất một lần.

Xử lý giá trị thiếu

df.isna().sum()               # đếm giá trị thiếu theo cột
df.dropna()                   # bỏ dòng có giá trị thiếu
df.dropna(subset=["luong"])   # chỉ xét cột luong
df.fillna({"luong": 0})       # điền giá trị thay thế
df["luong"].fillna(df["luong"].median())   # điền bằng trung vị

Đừng vội xóa dòng thiếu dữ liệu. Hãy tìm hiểu vì sao thiếu trước: dữ liệu thiếu có hệ thống (ví dụ chỉ thiếu ở một chi nhánh) mang thông tin quan trọng, và việc xóa đi có thể làm sai lệch kết quả phân tích.

Gom nhóm và tổng hợp

df.groupby("phong_ban")["luong"].mean()

df.groupby("phong_ban").agg(
    so_nguoi=("ten", "count"),
    luong_tb=("luong", "mean"),
    luong_cao_nhat=("luong", "max"),
    tuoi_tb=("tuoi", "mean"),
).round(0)

Cách viết agg với tên cột rõ ràng như trên dễ đọc hơn nhiều so với truyền một dictionary lồng nhau, và kết quả trả về có tên cột sạch sẽ ngay.

Nối bảng

nhan_vien = pd.DataFrame({"id": [1, 2, 3], "ten": ["An", "Bình", "Cường"]})
luong = pd.DataFrame({"id": [1, 2, 4], "so_tien": [15, 12, 20]})

pd.merge(nhan_vien, luong, on="id")                 # inner join
pd.merge(nhan_vien, luong, on="id", how="left")     # left join
pd.merge(nhan_vien, luong, on="id", how="outer")    # full outer join

Sau mỗi lần nối bảng, hãy kiểm tra số dòng. Nếu kết quả có nhiều dòng hơn bảng gốc, nhiều khả năng khóa nối bị trùng lặp — một lỗi âm thầm làm sai lệch mọi con số phía sau.

print(f"Trước: {len(nhan_vien)}, sau khi nối: {len(ket_qua)}")

Tạo cột mới

df["luong_nam"] = df["luong"] * 12
df["nhom_tuoi"] = pd.cut(
    df["tuoi"],
    bins=[0, 30, 40, 100],
    labels=["Dưới 30", "30-40", "Trên 40"],
)

Tránh dùng apply khi có sẵn phép toán vector hóa. Đoạn này chậm:

df["luong_nam"] = df["luong"].apply(lambda x: x * 12)   # chậm

Đoạn này nhanh hơn đáng kể trên dữ liệu lớn:

df["luong_nam"] = df["luong"] * 12                       # nhanh

Bảng tổng hợp

pd.pivot_table(
    df,
    values="luong",
    index="phong_ban",
    columns="nhom_tuoi",
    aggfunc="mean",
    fill_value=0,
)

Nếu bạn quen dùng PivotTable trong Excel, hàm này làm đúng việc đó — nhưng lặp lại được, ghi lại được trong code, và chạy trên dữ liệu lớn hơn nhiều so với giới hạn của Excel.

Xuất kết quả

df.to_csv("ket_qua.csv", index=False, encoding="utf-8-sig")
df.to_excel("ket_qua.xlsx", index=False, sheet_name="Tổng hợp")

Tham số index=False tránh việc thêm một cột số thứ tự vô nghĩa vào file xuất ra. Với file Excel cần mở bằng bản tiếng Việt, utf-8-sig giúp hiển thị đúng dấu tiếng Việt.

Khi dữ liệu quá lớn

Pandas nạp toàn bộ dữ liệu vào bộ nhớ. Khi file vượt quá dung lượng RAM, bạn có vài lựa chọn: đọc theo từng khối bằng tham số chunksize, chỉ đọc những cột cần dùng qua usecols, hoặc chuyển sang thư viện được thiết kế cho dữ liệu lớn như Polars hay Dask.

for khoi in pd.read_csv("file_lon.csv", chunksize=100_000):
    xu_ly(khoi)