Lập trình
xử lý dữ liệu

Học kì 1, năm học 2026–2027

Viện Trí tuệ nhân tạo, Trường ĐH Công nghệ, ĐHQGHN

Bài 10
Làm sạch dữ liệu
có cấu trúc

Phát hiện vấn đề, chọn cách xử lý và kiểm tra kết quả.

Bài trước

Trước bài thi giữa kỳ, bạn đã học cách đọc dữ liệu, tính toán với bảng, xử lý chuỗi và ngày giờ.

Code chạy được vẫn có thể cho kết quả sai nếu dữ liệu thiếu, giá trị bất thường hoặc bản ghi trùng chưa được xử lý phù hợp.

Tổng quan

  1. Giá trị thiếu
  2. Giá trị bất thường
  3. Trùng lặp
  4. Bộ quy tắc kiểm tra dữ liệu

1.
Giá trị thiếu

Chọn cách xử lý theo ý nghĩa của cột và câu hỏi phân tích.

Dữ liệu dùng trong bài

Santiago, mốc chụp 29/06/2026: 18.534 chỗ ở.
Giá tính bằng CLP/đêm.

Giữ cột price gốc để đối chiếu. Cách đổi giá dạng chuỗi đã học ở Bài 7.

Đếm giá trị thiếu theo cột

isna() nhận diện giá trị thiếu (NaN, pd.NA, None). Trung bình của cột boolean là tỷ lệ True.

Trước khi quyết định bỏ hay điền, cần xem những hàng nào thiếu.

Chỗ ở nào thiếu điểm đánh giá?

crosstab đếm số hàng theo từng cặp giá trị của hai cột. Cả 3.291 chỗ ở thiếu điểm đều chưa có đánh giá nào.

Điền 0 sẽ biến “chưa được đánh giá” thành một điểm số.

Chỉ bỏ hàng thiếu cột cần dùng

Ví dụ: tính giá trung bình của các chỗ ở đã có giá.

subset chỉ định cột cần kiểm tra. Bỏ subset thì dropna() mặc định bỏ hàng có ô thiếu ở bất kỳ cột nào.

Bài tập 1: So sánh giá giữa hai kỳ

Bảng bang ghi giá của cùng ba chỗ ở qua hai kỳ (giả lập, USD/đêm); P3 thiếu giá kỳ T6.

Trung bình giảm từ 200 xuống 165.

So sánh lại giá trung bình, chỉ dùng những chỗ ở có giá ở cả hai kỳ.

Điền một giá trị quy ước

Ví dụ giả lập: ghi rõ “Chưa rõ” ở chỗ ở chưa biết loại phòng.

fillna chỉ thay ô thiếu. “Chưa rõ” là nhãn quy ước, chưa phải loại phòng được xác định.

Với cột số, chỉ điền 0 khi có căn cứ rằng ô thiếu có nghĩa là không phát sinh.

Điền bằng giá trị phổ biến nhất

📖 Tự học

Giá trị xuất hiện nhiều nhất gọi là mốt (mode).

mode() bỏ qua NA và trả về một Series. Ở đây có một mốt, lấy bằng iloc[0].

P2 được ước lượng là phòng riêng. Cách này làm tăng tỷ lệ của loại phòng vốn phổ biến nhất.

Chọn trung bình hay trung vị?

Giá giả lập của sáu chỗ ở (USD/đêm):

Giá 620 kéo trung bình lên 200; trung vị là 100. Trung vị ít bị ảnh hưởng bởi giá trị cực đoan.

Cả hai cách đều giả định các giá đã biết có thể dùng để ước lượng giá còn thiếu.

Điền giá trị thiếu bằng trung vị

Một lựa chọn là dùng trung vị số phòng ngủ đã biết để điền ô thiếu.

fillna chỉ thay ô thiếu. Giá trị điền là ước lượng, nên giữ cột gốc để đối chiếu.

Điền theo nhóm tương tự

Số phòng ngủ và sức chứa của các chỗ ở (giả lập):

transform trả trung vị của nhóm về từng hàng. Nhóm sức chứa 6 toàn NA nên vẫn chưa điền được.

Điền số phòng ngủ theo sức chứa

Áp dụng cách điền theo nhóm với dữ liệu Santiago:

Mọi nhóm sức chứa đều có số phòng ngủ đã biết nên không còn ô thiếu. Cách này giả định chỗ ở cùng sức chứa có số phòng ngủ gần nhau.

Lấy giá trị đã biết trước hoặc sau

📖 Tự học

Giá theo tháng của một chỗ ở (giả lập, USD/đêm):

ffill() lấy giá trị đã biết gần nhất phía trước; bfill() lấy phía sau.

Cách điền này giả định giá chưa thay đổi trong đoạn bị thiếu.

Điền riêng từng chỗ ở

📖 Tự học

Bảng giả lập có hai chỗ ở qua ba kỳ; giá tính bằng USD/đêm.

groupby("id") tránh lấy giá của chỗ ở khác. limit=1 chỉ điền tối đa một ô trong mỗi đoạn thiếu liên tiếp.

Nội suy giữa hai giá trị đã biết

📖 Tự học

Nhiệt độ đo cùng giờ tại một trạm (giả lập, °C):

NgàyNhiệt độ
01/0620
02/06Thiếu
04/0626

Nội suy tuyến tính giả định nhiệt độ thay đổi đều giữa hai ngày đã biết.

Từ 01/06 đến 04/06: tăng 6°C trong 3 ngày. Ước lượng ngày 02/06: 20 + 6 × 1/3 = 22°C.

Nội suy theo khoảng thời gian

📖 Tự học

method="time" tính theo khoảng cách thời gian; cần chỉ mục ngày giờ đã sắp xếp.

limit_area="inside" chỉ điền giữa hai giá trị đã biết. Ngày 05/06 không có giá trị phía sau nên giữ NA.

Chọn cách điền theo dữ liệu

Cách điềnCăn cứ cần có
Giá trị quy ướcGhi rõ chưa biết, hoặc nguồn quy định giá trị thay thế.
Trung bình / trung vịCác giá trị đã biết có thể đại diện cho ô thiếu.
Giá trị phổ biến nhấtCó cơ sở ước lượng bằng nhãn phổ biến.
Theo nhómNhóm liên quan đến cột cần điền và có đủ dữ liệu.
Giá trị trước / sauGiá trị ít thay đổi trong đoạn thiếu.
Nội suyĐại lượng có thể biến đổi đều trong đoạn thiếu.

Nếu chưa có căn cứ để điền, có thể giữ NA và xử lý riêng khi phân tích.

Kiểm tra thống kê sau khi điền

Vẫn dùng giá giả lập [80, 90, 100, 110, 620, NA]:

Điền trung bình giữ nguyên trung bình nhưng làm giảm độ lệch chuẩn. Có thêm giá trị chưa có nghĩa là có thêm quan sát thật.

Giữ cột gốc và đánh dấu ô đã điền. Có thể che thử một số giá trị đã biết, rồi so giá trị điền với giá trị thật.

2.
Giá trị bất thường

Phát hiện giá trị bất thường, chọn cách xử lý và so tác động.

Giá trị sai hay chỉ khác thường?

Trường hợp Căn cứ để đánh giá
Điểm 6 trên thang từ 0 đến 5 Ngoài miền giá trị cho phép
Yêu cầu ở ít nhất 730 đêm Rất dài, cần kiểm tra quy định của nguồn
Giá cao hơn hầu hết chỗ ở Có thể là phân khúc khác hoặc lỗi dữ liệu

Ngoại lai (outlier) là giá trị khác xa phần lớn dữ liệu. Ngoại lai chưa chắc là lỗi.

Kiểm tra miền giá trị

Với điểm đánh giá, quy tắc đang dùng là 0 ≤ điểm ≤ 5.

between(0, 5) lấy cả hai biên. notna() tách giá trị thiếu khỏi giá trị nằm ngoài miền.

Đánh dấu giá trị sai trước khi thay

Điểm 6 và −1 nằm ngoài thang 0–5. Nếu chưa biết điểm đúng, có thể chuyển thành giá trị thiếu.

mask(điều_kiện) thay các ô có điều kiện True bằng NA; các ô còn lại giữ nguyên.

Giữ diem và cờ sai_mien để biết ô nào vốn thiếu, ô nào bị loại vì sai miền.

Độ lệch so với trung bình

📖 Tự học

z-score = (giá trị − trung bình) / độ lệch chuẩn. Giá trị có |z| lớn nằm xa trung bình.

Khối lượng các gói hàng (giả lập, gam):

Ngưỡng |z| > 3 thường dùng để sàng lọc dữ liệu gần đối xứng. Với giá có phân phối lệch mạnh, trung bình và độ lệch chuẩn đều bị giá cực đoan kéo lên.

Quan sát phân phối giá

Phân phối giá Santiago trên thang gốc và thang log10

Trên thang log₁₀, 4 ứng với 10.000 CLP; 5 ứng với 100.000 CLP. Thang log giúp nhìn rõ phân phối khi các giá chênh nhau rất nhiều lần.

Ngưỡng theo khoảng tứ phân vị

Q1 và Q3 là phân vị (percentile) 25% và 75%: khoảng 25% giá ≤ Q1, 75% giá ≤ Q3. IQR = Q3 − Q1.

Thường gắn cờ giá dưới Q1 − 1,5 × IQR hoặc trên Q3 + 1,5 × IQR.

Tính ngưỡng riêng theo loại chỗ ở

Phòng riêng và nguyên căn có mặt bằng giá khác nhau. Tính IQR trên các giá dương đã biết của từng loại:

unstack() đưa hai phân vị thành hai cột 0.25 và 0.75. Nhóm ít quan sát cho ngưỡng kém ổn định hơn.

Dùng ngưỡng nào cho mỗi hàng?

map tra ngưỡng theo loại chỗ ở của từng hàng. Bảng trên đếm số giá vượt ngưỡng.

Với phòng riêng, ngưỡng chung bỏ sót nhiều giá cao so với cùng loại. Các hàng bị cờ vẫn cần được kiểm tra.

Xem lại một mức giá cao

Cần đối chiếu nguồn, đơn vị và loại chỗ ở trước khi sửa giá. Nếu chưa xác minh được, giữ cờ và xem kết quả nhạy thế nào với giá này.

Chọn cách xử lý giá trị bất thường

Đã biết gì?Có thể xử lý thế nào?
Xác minh được giá trị đúngSửa theo nguồn và lưu giá trị cũ.
Biết là sai, chưa biết giá trị đúngChuyển thành NA, giữ cờ lý do.
Hiếm gặp nhưng có thể đúngGiữ lại; so kết quả có và không có nó.
Ngoài phạm vi phân tíchLoại khỏi phép tính đó, ghi rõ điều kiện.
Cần hạn chế ảnh hưởng của giá caoThử chặn ở một ngưỡng, rồi đo tác động.

Quyết định dựa vào ý nghĩa dữ liệu và mục đích phân tích; ngưỡng thống kê chỉ cung cấp thêm căn cứ.

Chặn giá trị ở một ngưỡng

Giá giả lập (USD/đêm). Thử chặn trên ở 200 để thấy cách clip hoạt động:

upper=200 thay giá lớn hơn 200 bằng 200; lower đặt ngưỡng dưới. Số hàng giữ nguyên.

Chặn theo phân vị thường gọi là winsorization. Ngưỡng 200 ở đây chỉ để minh hoạ, không phải quy tắc giá hợp lý.

So sánh bỏ hàng và chặn giá

Ngưỡng IQR chung gắn cờ 8,5% giá, quá nhiều để xem lại từng hàng. Thử với P99 (phân vị 99%):

Bỏ hàng làm giảm số giá; chặn giữ đủ số giá nhưng thay các giá cao. Cả hai đều làm giảm trung bình, chưa chứng minh giá gốc là sai.

Bài tập 2: Giá tăng đồng loạt

Một bộ kiểm tra tính lại P99 ở mỗi kỳ, rồi gắn cờ giá lớn hơn P99 của kỳ đó.

Kỳ sau giữ nguyên các chỗ ở, nhưng mọi giá đều tăng gấp đôi.

Dựa vào tỷ lệ bị gắn cờ, bộ kiểm tra này có phát hiện được đợt tăng giá không?

3.
Trùng lặp

Chọn khoá, tìm mâu thuẫn và giữ bản ghi có căn cứ.

Chọn khoá của bảng

Bảng giả lập lich_su lưu một giá cho mỗi chỗ ở, mỗi kỳ.

id ky gia
P1 T5 100
P1 T6 120
P1 T6 120
P2 T5 80
P2 T6 90
P2 T6 95

Khoá dùng để phân biệt từng bản ghi; ở đây là cặp (id, ky).

Chuẩn hoá mã trước khi tìm trùng

Ví dụ giả lập: nguồn quy định mã chỗ ở không phân biệt hoa thường, bỏ khoảng trắng hai đầu.

Giữ mã gốc để đối chiếu. Chỉ chuẩn hoá theo quy ước của nguồn; không tự bỏ số 0 đầu mã hay đổi hoa thường cho mọi loại mã.

Tìm tất cả hàng trùng khoá

subset chọn các cột đem so. keep=False đánh dấu mọi hàng thuộc nhóm trùng.

Mặc định keep="first" không đánh dấu hàng xuất hiện đầu tiên.

Bỏ bản ghi nhập lặp

Các hàng giống nhau ở mọi cột có thể là cùng một bản ghi được nhập lại.

drop_duplicates() mặc định giữ bản đầu của mỗi nhóm hàng giống nhau. Hai giá của P2 vẫn khác nhau: cần căn cứ để chọn bản đúng.

Tìm giá mâu thuẫn trong cùng khoá

Trong bảng lich_su, hai hàng P1/T6 cùng giá; hai hàng P2/T6 ghi giá khác nhau.

nunique đếm số giá trị khác nhau, mặc định bỏ NA. transform đưa số đếm về từng hàng của nhóm.

Cần xác minh hoặc có quy tắc chọn nguồn. Lấy trung bình hai giá chưa chắc khôi phục được giá đúng.

Gộp các thông tin bổ sung

📖 Tự học

Hai bản ghi của cùng chỗ ở, cùng kỳ (giả lập). Các giá trị đã biết không mâu thuẫn:

first() lấy giá trị đầu tiên không thiếu ở từng cột. Kết quả có thể ghép từ nhiều hàng.

Bài tập 3: Giữ giá cập nhật sau cùng

Mỗi hàng của bảng lich_cap_nhat là một lần cập nhật giá (giả lập). Cột cap_nhat có kiểu ngày giờ, năm 2026.

id ky cap_nhat gia
P1 T6 29/06 10:00 120
P2 T6 29/06 08:00 90
P1 T5 31/05 07:00 100
P1 T6 29/06 09:00 110

Với mỗi cặp (id, ky), chọn giá cập nhật sau cùng, bất kể thứ tự hàng trong bảng.

Khi thời điểm cập nhật cũng trùng

📖 Tự học

Giả sử P1/T6 có hai giá khác nhau nhưng cùng thời điểm cập nhật (giả lập):

Chọn theo thời điểm cập nhật như Bài tập 3 vẫn chưa có căn cứ chọn giá 120 hay 125. Cần thêm nguồn ưu tiên, phiên bản hoặc xác minh lại.

Trùng lặp phát sinh khi ghép bảng

Ví dụ giả lập: bảng tra vùng ghi hai hàng cho khu A.

P1 khớp hai hàng nên xuất hiện hai lần. Nếu mỗi khu chỉ được có một vùng, thêm validate="many_to_one" để kiểm khoá bên phải là duy nhất.

Sửa bảng tra cứu trước khi ghép lại. Xoá một hàng ở kết quả có thể giữ nhầm vùng.

4.
Bộ quy tắc kiểm tra dữ liệu

Đảm bảo chất lượng dữ liệu (quality assurance, QA): kiểm cấu trúc, quan hệ giữa dữ liệu và kết quả sau xử lý.

Kiểm tra cấu trúc đầu vào

Trước khi so giá với ngưỡng, bảng phải đủ cột và các cột dùng để tính phải là số.

is_numeric_dtype kiểm kiểu số. Thiếu cột hoặc sai kiểu thì dừng với thông báo; không bỏ qua phép kiểm rồi báo 0 lỗi.

Kiểm tra quan hệ giữa các cột

📖 Tự học

Quy định đặt phòng giả lập: số đêm tối thiểu ≤ số đêm tối đa.

Mỗi cột đều có thể chứa số hợp lệ, nhưng cặp 3 và 2 mâu thuẫn. Hàng thiếu số đêm được gắn cờ riêng vì chưa đủ dữ liệu để so.

Kiểm tra mã có ở bảng liên quan

Mã chỗ ở của mỗi đánh giá phải có trong bảng chỗ ở. Ví dụ giả lập, hai bảng cùng phạm vi:

isin kiểm mã thuộc danh sách cho phép. R2 có mã P9 không tìm thấy; R3 thiếu mã nên được tách riêng.

Đối chiếu số liệu giữa hai bảng

Tại cùng mốc chụp, bảng chỗ ở ghi số đánh giá; bảng đánh giá cho phép đếm lại (giả lập):

reindex(..., fill_value=0) giữ cả P3 không có đánh giá. P2 lệch 1: cần kiểm thời điểm chụp, phạm vi và cách đếm ở nguồn.

Mô tả quy tắc trước khi viết code

Gom các quy tắc cho bảng chỗ ở Santiago. Mỗi quy tắc ghi rõ căn cứ và cách xử lý.

Quy tắc Căn cứ Cách xử lý
Thiếu giá Không tính được giá Bỏ khỏi phép tính giá
Giá trên P99 tham chiếu Giá cao bất thường Xem hàng bị cờ, so tác động
Thiếu số đêm tối thiểu Không kiểm được quy định Đếm riêng, xem nguồn
Số đêm tối thiểu > 365 Ít khi thuê hơn một năm Xem quy định của nguồn
Trùng id (một mốc chụp) Mỗi chỗ ở một bản ghi Xem cả nhóm trùng

Chọn ngưỡng theo mục đích kiểm tra

Muốn kiểm tra Cách dùng ngưỡng
Giá nào cao trong từng kỳ? Tính P99 riêng trong kỳ đó
Giá có vượt mức tham chiếu cũ? Giữ P99 của kỳ tham chiếu

Khi so qua các kỳ, ghi lại nguồn ngưỡng, đơn vị và mốc chụp. Thay thành phố hoặc tiền tệ thì phải xem lại ngưỡng.

Viết hàm trả về các cờ

Mỗi cột là một quy tắc, mỗi hàng ứng với một hàng của d. Hàm trả bảng boolean, không sửa dữ liệu đầu vào.

NaN > 365 cho False, nên số đêm thiếu cần cờ riêng dem_thieu.

Báo cáo số hàng bị gắn cờ

Tỷ lệ ở đây chia cho toàn bộ 18.534 hàng. id_trung bằng 0 chưa cho biết quy tắc có bắt được id trùng hay không.

Một hàng có thể bị nhiều cờ

Bảng cờ co_mau giả lập:

Mã gia_cao dem_tren_365
P1 True True
P2 True False
P3 False False

3 lượt gắn cờ, 2 hàng cần xem.

any(axis=1) đánh dấu hàng có ít nhất một cờ True.

Lưu cả những hàng cần kiểm tra

join ghép cờ theo chỉ mục hàng; add_prefix thêm co_ vào tên cột để phân biệt với dữ liệu gốc.

Báo cáo tổng cho biết có bao nhiêu vấn đề. Bảng chi tiết giữ mã, giá trị gốc và lý do để kiểm tra từng hàng.

Thử quy tắc trên dữ liệu nhỏ

Thử cả giá trị thiếu, giá trị đúng bằng ngưỡng, vượt ngưỡng và khoá trùng.

Kiểm tra tác động của bước làm sạch

Quay lại ví dụ điểm đánh giá ở slide «Đánh dấu giá trị sai trước khi thay».

Số điểm sai miền giảm, nhưng số ô thiếu tăng. assert kiểm các ô ngoài phạm vi sửa vẫn giữ nguyên.

Sau mỗi bước, kiểm lại cả quy tắc cần sửa lẫn phần dữ liệu phải được giữ lại.

Tổng kết

  • Giá trị thiếu: chọn cách bỏ hoặc điền theo dữ liệu, rồi kiểm tra tác động đến thống kê.
  • Ngoại lai: chọn ngưỡng theo phân phối và nhóm; so tác động khi bỏ hoặc chặn giá trị.
  • Trùng lặp: phân biệt bản nhập lặp, thông tin bổ sung và giá trị mâu thuẫn.
  • QA: kiểm đầu vào, quan hệ giữa các cột/bảng; lưu cờ và kiểm lại sau mỗi bước.

Làm việc với AI thì sao?

🤖 Làm việc với AI
AI làm tốt Viết mặt nạ, hàm kiểm tra và bảng báo cáo từ yêu cầu rõ ràng.
AI hay sai Điền mọi ô thiếu bằng trung bình, xoá hàng theo IQR, ghép bảng mà không kiểm khoá, hoặc giữ bản ghi cuối mà chưa kiểm tra thứ tự.

Kiểm chứng thế nào?

  • Thử trên bảng nhỏ có đáp án biết trước, gồm cả giá trị ở biên và giá trị thiếu.
  • Xem các hàng bị cờ; đối chiếu nguồn, đơn vị và ý nghĩa của từng cột.
  • So số hàng, thống kê trước và sau xử lý; giải thích thay đổi.

Thiếu cột bắt buộc thì báo lỗi. Chưa chạy một quy tắc không có nghĩa là quy tắc đó có 0 vi phạm.

Đọc thêm