Dữ liệu trùng lặp (Duplicates) là hiện tượng một bản ghi xuất hiện nhiều lần trong tập dữ liệu với thông tin hoàn toàn giống nhau trên tất cả các cột, hoặc giống nhau ở các thuộc tính định danh quan trọng.
Trước khi thực hiện bất kỳ thao tác xóa nào, nhà phân tích dữ liệu cần phân định rạch ròi hai loại trùng lặp có bản chất hoàn toàn khác nhau để tránh làm mất mát thông tin:
Trùng lặp cơ học/hệ thống (Lỗi dữ liệu): Đây là những nhiễu kỹ thuật phát sinh ngoài ý muốn. Nguyên nhân phổ biến có thể do lỗi gộp dữ liệu (merge) từ nhiều nguồn, lỗi nhập liệu nhiều lần, hoặc trong thương mại điện tử là do nghẽn mạng (Network Timeout) khiến khách hàng nhấp đúp (double-click) vào nút thanh toán. Việc tồn tại các bản ghi này sẽ làm sai lệch các thống kê tổng hợp (như thổi phồng doanh thu) và gây hiện tượng "học vẹt" (overfitting) cho các mô hình Học máy.
Trùng lặp hợp lệ (Hành vi thực tế): Xảy ra khi dữ liệu lặp lại một cách hợp lý theo logic kinh doanh. Ví dụ, trong tập dữ liệu tàu Titanic, nhiều hành khách trong cùng một gia đình có thể dùng chung một mã vé (Ticket) - đây là trùng lặp hợp lệ. Chỉ khi nào mã định danh cá nhân (PassengerId) bị trùng thì đó mới chắc chắn là lỗi hệ thống.
Tư duy chuẩn mực trong Data Science là không bao giờ xóa dữ liệu ngay lập tức khi vừa phát hiện trùng lặp. Thay vào đó, cần thực hiện thao tác "Đánh dấu" để kiểm tra chéo (Audit) và tìm hiểu nguyên nhân gốc rễ.
Thư viện Pandas cung cấp hàm df.duplicated() để quét và trả về giá trị True cho các dòng bị trùng lặp. Bằng cách sử dụng tham số keep=False, ta có thể hiển thị tất cả các bản ghi (bao gồm cả bản gốc và bản sao) để đối chiếu trực quan.
Tùy vào tình huống, Pandas hỗ trợ nhiều kỹ thuật xử lý khác nhau:
Xóa trùng lặp hoàn toàn: Dùng hàm df.drop_duplicates(). Mặc định với tham số keep='first', Pandas sẽ giữ lại dòng đầu tiên và xóa các bản sao phía sau.
Giữ bản cập nhật mới nhất: Nếu dữ liệu được sắp xếp theo thời gian và dòng xuất hiện sau là bản cập nhật mới nhất, ta dùng tham số keep='last' để giữ lại quan sát cuối cùng.
Xóa dựa trên một số cột (Subset): Khi dữ liệu chỉ cần duy nhất trên một trường định danh (như Mã khách hàng hay Mã hóa đơn), ta dùng subset=['Cột_Khóa'] để thanh lọc.
Gộp dữ liệu (Combine): Trong trường hợp các dòng trùng lặp mã định danh nhưng lại chứa các thông tin bổ sung khác nhau (do thu thập từ nhiều nguồn), thay vì xóa, ta có thể dùng groupby().agg() để gộp các giá trị lại (ví dụ: lấy trung bình chi tiêu, gộp chuỗi sở thích).
Để minh họa cho quy trình trên, chúng ta sử dụng PaySim Dataset – một bộ dữ liệu mô phỏng các giao dịch tài chính trên thiết bị di động (Mobile Money) với quy mô hơn 6,3 triệu bản ghi, được thiết kế chuyên biệt cho bài toán phát hiện gian lận (Fraud Detection).
Trong bối cảnh hệ thống thanh toán, nếu một giao dịch chuyển tiền (TRANSFER) hoặc rút tiền (CASH_OUT) bị lỗi mạng và ghi nhận lặp lại 2-3 lần, mô hình học máy có thể đánh giá sai quy mô của dòng tiền gian lận.
Dưới đây là đoạn mã mô phỏng một trích đoạn dữ liệu PaySim chứa lỗi hệ thống. Bạn có thể sao chép và chạy trực tiếp đoạn code này trên Google Colab để quan sát luồng xử lý:
import pandas as pd
# 1. TẠO TRÍCH ĐOẠN DỮ LIỆU PAYSIM MÔ PHỎNG LỖI TRÙNG LẶP
data_paysim = {
'step': [1, 2, 3, 4, 5], # 1 step = 1 giờ
'type': ['TRANSFER', 'TRANSFER', 'CASH_OUT', 'PAYMENT', 'PAYMENT'],
'amount': [181.0, 181.0, 181.0, 116.68, 116.68],
'nameOrig': ['C1305486145', 'C1305486145', 'C840083671', 'C712410124', 'C712410124'],
'isFraud': [0, 0, 1, 0, 0] # 1: Gian lận, 0: Hợp lệ
}
df_paysim = pd.DataFrame(data_paysim)
# 2. KIỂM TRA SỐ LƯỢNG TRÙNG LẶP
print("--- BƯỚC 1: KIỂM TRA SỐ LƯỢNG TRÙNG LẶP ---")
dup_count = df_paysim.duplicated().sum()
print(f"Tổng số giao dịch bị trùng lặp hệ thống: {dup_count}\n")
# 3. TƯ DUY AUDIT: ĐÁNH DẤU VÀ KIỂM TRA CHÉO
print("--- BƯỚC 2: KIỂM TRA CHI TIẾT CÁC DÒNG TRÙNG LẶP ---")
# Tham số keep=False hiển thị cả bản gốc và bản sao để đối chiếu
df_paysim['is_duplicate'] = df_paysim.duplicated(keep=False)
print(df_paysim[df_paysim['is_duplicate'] == True])
print("\n")
# 4. LÀM SẠCH: KHỬ TRÙNG LẶP (DEDUPLICATION)
print("--- BƯỚC 3: KẾT QUẢ SAU KHI LÀM SẠCH ---")
# Bỏ cột audit và xóa dòng trùng lặp, giữ lại giao dịch đầu tiên
df_clean = df_paysim.drop(columns=['is_duplicate']).drop_duplicates(keep='first')
print(f"Số giao dịch ban đầu: {len(df_paysim)}")
print(f"Số giao dịch hợp lệ sau làm sạch: {len(df_clean)}")
print("\nDữ liệu tinh khiết:")
print(df_clean)
💡 Insights và Ý nghĩa quản trị: Việc khử trùng lặp trong dữ liệu tài chính không chỉ để "làm nhẹ" máy chủ mà mang ý nghĩa sống còn về mặt nghiệp vụ. Trong bộ dữ liệu PaySim, tỷ lệ gian lận thực tế chỉ chiếm chưa tới 0,6% tổng số giao dịch. Nếu để lọt các giao dịch gian lận bị nhân bản do lỗi kỹ thuật, mô hình học máy sẽ gặp hiện tượng "quá khớp" (Overfitting) – học thuộc lòng một vài mẫu gian lận lặp đi lặp lại thay vì học quy luật tổng quát. Xử lý triệt để Duplicate Values là bức tường lửa bảo vệ tính toàn vẹn của mô hình cảnh báo sớm, giúp các định chế tài chính định lượng chính xác thiệt hại và tránh khóa nhầm tài khoản của khách hàng do các cảnh báo rủi ro ảo.
Ví dụ*: Loại bỏ trùng lặp trên một số cột (mặc định giữ hàng đầu tiên)
import pandas as pd
# Tạo dữ liệu mẫu
data = {
'Ten': ['An', 'Binh', 'Cuong', 'An', 'Linh'],
'Chi tieu hang thang (trieu dong)': [10.0, 15.0, 12.5, 12.0, 15.0],
'Tuoi': [20, 21, 22, 20, 24],
'So thich': ['Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách', 'Đọc sách']
}
# Tạo DataFrame từ dữ liệu mẫu
df = pd.DataFrame(data)
# Loại bỏ các hàng trùng lặp dựa trên cột 'Ten' và 'Tuoi'
df_no_duplicates_specific = df.drop_duplicates(subset=['Ten', 'Tuoi'])
# Hiển thị DataFrame sau khi xử lý
#print("DataFrame sau khi loại bỏ các hàng trùng lặp:")
print(df_no_duplicates_specific)
Ví dụ*: Loại bỏ trùng lặp trên một số cột (giữ hàng cuối)
import pandas as pd
# Tạo dữ liệu mẫu
data = {
'Ten': ['An', 'Binh', 'Cuong', 'An', 'Linh'],
'Chi tieu hang thang (trieu dong)': [10.0, 15.0, 12.5, 10.0, 15.0],
'Tuoi': [20, 21, 22, 20, 24],
'So thich': ['Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách', 'Đọc sách']
}
# Tạo DataFrame từ dữ liệu mẫu
df = pd.DataFrame(data)
# Loại bỏ các hàng trùng lặp và giữ lại hàng cuối cùng
df_no_duplicates_last = df.drop_duplicates(subset=['Ten', 'Tuoi'], keep='last')
# Hiển thị DataFrame sau khi xử lý
print("DataFrame sau khi giữ lại hàng cuối cùng cho các giá trị trùng lặp:")
print(df_no_duplicates_last)
Ví dụ*: Đánh dấu trùng lặp mà không xóa
import pandas as pd
# Tạo dữ liệu mẫu
data = {
'Ten': ['An', 'Binh', 'Cuong', 'An', 'Linh'],
'Chi tieu hang thang (trieu dong)': [10.0, 15.0, 12.5, 10.0, 15.0],
'Tuoi': [20, 21, 22, 20, 24],
'So thich': ['Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách', 'Đọc sách']
}
# Tạo DataFrame từ dữ liệu mẫu
df = pd.DataFrame(data)
# Đánh dấu các hàng trùng lặp mà không xóa chúng
df['is_duplicate'] = df.duplicated(subset=['Ten', 'Tuoi'])
# Hiển thị DataFrame sau khi thêm cột 'is_duplicate'
print("DataFrame sau khi đánh dấu các hàng trùng lặp:")
print(df)
Ví dụ*: đếm số lượng trùng lặp
import pandas as pd
# Tạo dữ liệu mẫu
data = {
'Ten': ['An', 'Binh', 'Cuong', 'An', 'Linh'],
'Chi tieu hang thang (trieu dong)': [10.0, 15.0, 12.5, 10.0, 15.0],
'Tuoi': [20, 21, 22, 20, 24],
'So thich': ['Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách', 'Đọc sách']
}
# Tạo DataFrame từ dữ liệu mẫu
df = pd.DataFrame(data)
# Đếm số lượng hàng trùng lặp dựa trên tất cả các cột
num_duplicates = df.duplicated().sum()
# Đếm số lượng hàng trùng lặp dựa trên một số cột nhất định (ví dụ: 'Ten' và 'Tuoi')
num_duplicates_specific = df.duplicated(subset=['Ten', 'Tuoi']).sum()
# Hiển thị kết quả
print(f"Số lượng hàng trùng lặp trong toàn bộ DataFrame: {num_duplicates}")
print(f"Số lượng hàng trùng lặp dựa trên cột 'Ten' và 'Tuoi': {num_duplicates_specific}")
Ví dụ*: tính tần suất suất hiện của các bảng ghi trùng lặp
import pandas as pd
# Tạo dữ liệu mẫu
data = {
'Ten': ['An', 'Binh', 'Cuong', 'An', 'Linh', 'Binh', 'Cuong', 'An'],
'Chi tieu hang thang (trieu dong)': [10.0, 15.0, 12.5, 10.0, 15.0, 15.0, 12.5, 10.0],
'Tuoi': [20, 21, 22, 20, 24, 21, 22, 20],
'So thich': ['Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách', 'Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách']
}
# Tạo DataFrame từ dữ liệu mẫu
df = pd.DataFrame(data)
# Tính tần suất xuất hiện của các bản ghi trùng lặp dựa trên các cột 'Ten' và 'Tuoi'
frequency = df.value_counts(subset=['Ten', 'Tuoi'])
# Hiển thị kết quả
print("Tần suất xuất hiện của các bản ghi trùng lặp (dựa trên 'Ten' và 'Tuoi'):")
print(frequency)
Ví dụ*: tạo bản sao chỉ chứa hàng trùng lặp để xử lý
import pandas as pd
# Tạo dữ liệu mẫu
data = {
'Ten': ['An', 'Binh', 'Cuong', 'An', 'Linh', 'Binh', 'Cuong', 'An'],
'Chi tieu hang thang (trieu dong)': [10.0, 15.0, 12.5, 10.0, 15.0, 15.0, 12.5, 10.0],
'Tuoi': [20, 21, 22, 20, 24, 21, 22, 20],
'So thich': ['Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách', 'Đọc sách', 'Xem phim', 'Đọc sách', 'Đọc sách']
}
# Tạo DataFrame từ dữ liệu mẫu
df = pd.DataFrame(data)
# Tạo bản sao chỉ chứa các hàng trùng lặp
df_duplicates_only = df[df.duplicated(keep=False)]
# Hiển thị DataFrame chỉ chứa các hàng trùng lặp
print("DataFrame chứa các hàng trùng lặp để kiểm tra:")
print(df_duplicates_only)