Dữ liệu khuyết thiếu (missing data) là tình trạng một hoặc nhiều biến trong tập dữ liệu không có giá trị quan sát, thường được biểu diễn dưới dạng NaN hoặc None trong Python. Trong khoa học thống kê, trước khi quyết định xử lý, nhà phân tích cần xác định cơ chế gây ra khuyết thiếu, được chia làm ba loại chính:
MCAR (Missing Completely At Random - Thiếu hoàn toàn ngẫu nhiên): Việc thiếu dữ liệu không phụ thuộc vào bất kỳ biến số nào (ví dụ: lỗi hệ thống vô tình làm mất một số bản ghi).
MAR (Missing At Random - Thiếu ngẫu nhiên có điều kiện): Dữ liệu thiếu phụ thuộc vào các biến khác đã được quan sát (ví dụ: khách hàng trẻ tuổi thường ít chịu khai báo thu nhập hơn người lớn tuổi).
MNAR (Missing Not At Random - Thiếu không ngẫu nhiên): Dữ liệu bị thiếu phụ thuộc vào chính bản chất của giá trị đó (ví dụ: những người có thu nhập cực "khủng" thường cố tình giấu và không khai báo thu nhập).
Bước đầu tiên trong quá trình làm sạch là phải "bắt mạch" dữ liệu. Thay vì chỉ nhìn vào những bảng số liệu khô khan, việc sử dụng các hàm của Pandas kết hợp với thư viện đồ họa Seaborn sẽ giúp ta nhanh chóng xác định cột nào bị thiếu và thiếu bao nhiêu phần trăm.
Đặc biệt, công cụ Bản đồ nhiệt (Heatmap) của Seaborn cho phép nhà phân tích nhìn thấy toàn cảnh vị trí của dữ liệu thiếu. Nếu xuất hiện các vạch sáng rải rác, dữ liệu có thể bị thiếu ngẫu nhiên; nếu xuất hiện các mảng sáng tập trung thành khối lớn, dữ liệu có khả năng bị lỗi theo hệ thống.
Dưới đây là đoạn mã hoàn chỉnh. Bạn có thể sao chép đoạn mã này, dán vào Google Colab và chạy ngay lập tức để xem biểu đồ Heatmap trông như thế nào:
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
# Tải dữ liệu mẫu Titanic trực tiếp từ Seaborn để chạy và xem kết quả ngay
df = sns.load_dataset('titanic')
# Đọc dữ liệu và đếm số lượng, tỷ lệ giá trị thiếu
missing = df.isnull().sum()
missing_percent = (missing / len(df)) * 100
print("Tỷ lệ dữ liệu khuyết thiếu (%):")
print(missing_percent[missing_percent > 0])
# Trực quan hóa dữ liệu thiếu bằng Heatmap
plt.figure(figsize=(10, 6))
sns.heatmap(df.isnull(), cbar=False, cmap='viridis')
plt.title('Bản đồ nhiệt Dữ liệu khuyết thiếu')
plt.show()
Sau khi chạy, bạn sẽ thấy cột deck (cabin) hiển thị một dải màu sáng rực rỡ, cảnh báo cho chúng ta biết rằng cột này đang bị thiếu một lượng dữ liệu khổng lồ (hơn 77%)!
Phương pháp xóa (Listwise deletion) thường được áp dụng khi tỷ lệ thiếu nhỏ hoặc khi biến bị thiếu đóng vai trò là "chìa khóa" không thể thay thế.
Hãy xét bộ dữ liệu bán lẻ Online Retail Dataset (download). Sau khi kiểm tra, hệ thống phát hiện cột CustomerID (Mã khách hàng) bị khuyết thiếu tới 24.93% (khoảng 135.080 giao dịch). Trong môi trường thương mại điện tử, đây là hiện tượng "khách vãng lai" (Guest Checkout) – người dùng mua hàng nhưng không đăng ký tài khoản.
Mặc dù 24.93% là một tỷ lệ lớn, nhưng nếu mục tiêu kinh doanh của chúng ta là phân tích giá trị vòng đời khách hàng (CLV) và xây dựng mô hình RFM (Recency, Frequency, Monetary), ta bắt buộc phải xóa các dòng này. Nếu không có mã định danh, ta không thể liên kết các hóa đơn rời rạc để biết khách hàng đó mua bao nhiêu lần hay chi tiêu tổng cộng bao nhiêu.
# Thư viện
import pandas as pd
# Đọc file
df_retail = pd.read_csv('https://drive.google.com/uc?export=download&id=1w6-kDdajl-9A_qIVZUW4AD0Par9oEgAg', encoding='latin1')
# Xóa các giao dịch không có mã khách hàng
df_retail_clean = df_retail.dropna(subset=['CustomerID'])
print(f"Số giao dịch giữ lại có thể định danh: {len(df_retail_clean)}")
Trong trường hợp việc xóa dòng làm mất đi quá nhiều thông tin quý giá, phương pháp điền thế (thay thế NaN bằng giá trị trung bình, trung vị hoặc mode) sẽ được sử dụng.
Tuy nhiên, thay vì điền một giá trị trung bình cho toàn bộ cột, kỹ thuật "Điền bù theo nhóm" (Group-based imputation) sẽ mang lại độ chính xác cao hơn rất nhiều. Lấy bộ dữ liệu Titanic Dataset làm ví dụ:
Cột age (Tuổi) bị thiếu khoảng 20% dữ liệu. Nếu ta lấy tuổi trung vị của toàn bộ hành khách để gán cho những người bị thiếu, ta sẽ làm sai lệch phân phối thực tế. Thay vào đó, ta sẽ nhóm hành khách theo hạng vé (pclass) và giới tính (sex), sau đó lấy tuổi trung vị của từng nhóm nhỏ này để bù đắp,.
Cột deck (tương đương với mã Cabin) bị khuyết thiếu đến hơn 77%. Khi một biến bị thiếu quá nhiều, ta không nên cố gắng điền số liệu giả. Phương án tốt nhất là tạo một biến mới mang tên has_cabin (1: có cabin, 0: không có cabin) để giữ lại yếu tố thông tin, sau đó xóa bỏ toàn bộ cột gốc,.
Dưới đây là đoạn mã hoàn chỉnh từ lúc khai báo thư viện đến khi làm sạch xong. Bạn có thể sao chép đoạn mã này, dán vào Google Colab và chạy ngay lập tức để xem kết quả:
# 1. KHAI BÁO THƯ VIỆN
import pandas as pd
import numpy as np
import seaborn as sns
# 2. TẢI DỮ LIỆU TỪ THƯ VIỆN SEABORN
# (Không cần tải file CSV về máy, dữ liệu sẽ được kéo trực tiếp vào Colab)
df_titanic = sns.load_dataset('titanic')
# Kiểm tra dữ liệu bị thiếu trước khi xử lý
print("--- TRƯỚC KHI XỬ LÝ ---")
print(df_titanic[['age', 'deck', 'embarked']].isnull().sum())
# 3. THỰC HIỆN ĐIỀN THẾ VÀ LÀM SẠCH DỮ LIỆU (IMPUTATION)
# 3.1. Bù tuổi (age) theo giá trị Trung vị (median) của từng nhóm Hạng vé (pclass) và Giới tính (sex)
df_titanic['age'] = df_titanic.groupby(['pclass', 'sex'])['age'].transform(lambda x: x.fillna(x.median()))
# 3.2. Xử lý cột 'deck' (thiếu >77%):
# Tạo biến nhị phân 'has_cabin' (1: có, 0: không) thay vì giữ giá trị gốc
df_titanic['has_cabin'] = df_titanic['deck'].notna().astype(int)
# Sau khi lấy được thông tin, ta xóa cột 'deck' đi
df_titanic_clean = df_titanic.drop(columns=['deck'])
# 3.3. Cột 'embarked' (Cảng lên tàu) thiếu rất ít (2 dòng):
# Điền bằng giá trị xuất hiện nhiều nhất (mode)
df_titanic_clean['embarked'] = df_titanic_clean['embarked'].fillna(df_titanic_clean['embarked'].mode())
# 4. KIỂM TRA LẠI KẾT QUẢ
print("\n--- SAU KHI XỬ LÝ ---")
print(df_titanic_clean[['age', 'has_cabin', 'embarked']].isnull().sum())
💡 Insights và Ý nghĩa kinh tế: Việc xử lý dữ liệu khuyết thiếu không phải là một công thức toán học cứng nhắc, mà đòi hỏi sự thấu hiểu về đặc thù nghiệp vụ. Việc biến sự khuyết thiếu của cột Cabin thành một biến mới (has_cabin) là ứng dụng nguyên lý "sự khuyết thiếu cũng là một dạng thông tin" (missingness as a feature). Chẳng hạn, trong dữ liệu kinh tế, một khách hàng cố tình từ chối khai báo thu nhập có thể mang đặc điểm rủi ro tài chính rất khác so với những người sẵn sàng cung cấp thông tin. Quá trình làm sạch thành công là khi nhà phân tích giữ được tính trung thực của các đại lượng kinh tế mà vẫn cung cấp đủ dữ liệu nguyên vẹn cho các thuật toán Học máy.