Bài thứ 4 trong chuỗi các bài viết về chủ đề Data Preparation cho các mô hình ML và là bài đầu tiên về Missing Data. Trong bài này, chúng ta sẽ tìm hiểu về vấn đề Missing Data.
Missing Data là hiện tượng thiếu hụt một vài giá trị trong tập dữ liệu. Những vị trí thiếu đó có thể được thể hiện bởi số 0, số âm, khoảng trắng, hoặc một ký tự đặc biệt nào đó (dấu ? chẳng hạn).
Nguyên nhân dẫn đến hiện tượng này có thể là do có sai sót trong quá trình thu thập dữ liệu, hoặc dữ liệu bị hỏng (corruption) trong quá trình lưu trữ và trao đổi.
Phần lớn các thuật toán ML không thể làm việc được với Missing Data, hoặc nếu có thì kết quả cũng không đáng tin cậy. Vì thế, chúng ta cần loại bỏ vấn đề này trước khi thực hiện các bước mô hình hóa dữ liệu.
Trước tiên, bạn hãy tải bộ dữ liệu Diabetes tại đây để thực hành trong phần này.
Để nhận diện Missing Data, các hay dùng nhất là sử dụng các hàm thống kê. Xem code dưới đây:
# load and summarize the dataset
from pandas import read_csv
# load the dataset
dataset = read_csv( 'pima-indians-diabetes.csv' , header=None)
# summarize the dataset
print(dataset.describe())
Đoạn code trên đọc Diabetes dataset và hiển thị các thông tin tóm tắt thống kê của nó:
0 1 2 3 4 5 6 7 8
count 768.000000 768.000000 768.000000 768.000000 768.000000 768.000000 768.000000 768.000000 768.000000
mean 3.845052 120.894531 69.105469 20.536458 79.799479 31.992578 0.471876 33.240885 0.348958
std 3.369578 31.972618 19.355807 15.952218 115.244002 7.884160 0.331329 11.760232 0.476951
min 0.000000 0.000000 0.000000 0.000000 0.000000 0.000000 0.078000 21.000000 0.000000
25% 1.000000 99.000000 62.000000 0.000000 0.000000 27.300000 0.243750 24.000000 0.000000
50% 3.000000 117.000000 72.000000 23.000000 30.500000 32.000000 0.372500 29.000000 0.000000
75% 6.000000 140.250000 80.000000 32.000000 127.250000 36.600000 0.626250 41.000000 1.000000
max 17.000000 199.000000 122.000000 99.000000 846.000000 67.100000 2.420000 81.000000 1.000000
Quan sát kết quả hiển thị bên trên, ta có thể thấy rằng có một vài cột có chứa giá trị 0 (thể hiện ở việc giá trị nhỏ nhất của cột đó là 0). Theo kiến thức chuyên môn trong lĩnh vực y học về bệnh tiểu đường thì 0 là giá trị không hợp lệ ở đây. Điều đó có nghĩa là, tại các vị trí có giá trị 0 chính là Missing Data.
Cụ thể hơn, Missing Data nằm ở các cột [1, 2, 3, 4, 5] tương ứng với [Plasma glucose concentration, Diastolic blood pressur, Triceps skinfold thickness, 2-Hour serum insulin, Body mass index]. Tại cột đầu tiên, giá trị 0 là hợp lệ.
Thử xác nhận lại điều này bằng cách kiểm tra 20 hàng đầu tiên của bộ dữ liệu:
# load the dataset and review rows
from pandas import read_csv
# load the dataset
dataset = read_csv( 'pima-indians-diabetes.csv' , header=None)
# summarize the first 20 rows of data
print(dataset.head(20))
Kết quả:
0 1 2 3 4 5 6 7 8
0 6 148 72 35 0 33.6 0.627 50 1
1 1 85 66 29 0 26.6 0.351 31 0
2 8 183 64 0 0 23.3 0.672 32 1
3 1 89 66 23 94 28.1 0.167 21 0
4 0 137 40 35 168 43.1 2.288 33 1
5 5 116 74 0 0 25.6 0.201 30 0
6 3 78 50 32 88 31.0 0.248 26 1
7 10 115 0 0 0 35.3 0.134 29 0
8 2 197 70 45 543 30.5 0.158 53 1
9 8 125 96 0 0 0.0 0.232 54 1
10 4 110 92 0 0 37.6 0.191 30 0
11 10 168 74 0 0 38.0 0.537 34 1
12 10 139 80 0 0 27.1 1.441 57 0
13 1 189 60 23 846 30.1 0.398 59 1
14 5 166 72 19 175 25.8 0.587 51 1
15 7 100 0 0 0 30.0 0.484 32 1
16 0 118 84 47 230 45.8 0.551 31 1
17 7 107 74 0 0 29.6 0.254 31 1
18 1 103 30 38 83 43.3 0.183 33 0
19 1 115 70 30 96 34.6 0.529 32 1
Khi làm việc với dữ liệu có Missing Data, chúng ta nên đánh dấu (mark) chúng bằng một giá trị là NaN, bởi vì các thư viện Pandas, Numpy, Scikit-learn đều bỏ qua giá trị này trong các tính toán của chúng. Để làm việc này, ta sử dụng hàm replace() của Pandas.
# example of review data with missing values marked with a nan
from numpy import nan
from pandas import read_csv
# load the dataset
dataset = read_csv( ' pima-indians-diabetes.csv ' , header=None)
# replace ' 0 ' values with ' nan '
dataset[[1,2,3,4,5]] = dataset[[1,2,3,4,5]].replace(0, nan)
# summarize the first 20 rows of data
print(dataset.head(20))
Kết quả hiển thị:
0 1 2 3 4 5 6 7 8
0 6 148.0 72.0 35.0 NaN 33.6 0.627 50 1
1 1 85.0 66.0 29.0 NaN 26.6 0.351 31 0
2 8 183.0 64.0 NaN NaN 23.3 0.672 32 1
3 1 89.0 66.0 23.0 94.0 28.1 0.167 21 0
4 0 137.0 40.0 35.0 168.0 43.1 2.288 33 1
5 5 116.0 74.0 NaN NaN 25.6 0.201 30 0
6 3 78.0 50.0 32.0 88.0 31.0 0.248 26 1
7 10 115.0 NaN NaN NaN 35.3 0.134 29 0
8 2 197.0 70.0 45.0 543.0 30.5 0.158 53 1
9 8 125.0 96.0 NaN NaN NaN 0.232 54 1
10 4 110.0 92.0 NaN NaN 37.6 0.191 30 0
11 10 168.0 74.0 NaN NaN 38.0 0.537 34 1
12 10 139.0 80.0 NaN NaN 27.1 1.441 57 0
13 1 189.0 60.0 23.0 846.0 30.1 0.398 59 1
14 5 166.0 72.0 19.0 175.0 25.8 0.587 51 1
15 7 100.0 NaN NaN NaN 30.0 0.484 32 1
16 0 118.0 84.0 47.0 230.0 45.8 0.551 31 1
17 7 107.0 74.0 NaN NaN 29.6 0.254 31 1
18 1 103.0 30.0 38.0 83.0 43.3 0.183 33 0
19 1 115.0 70.0 30.0 96.0 34.6 0.529 32 1
Sau khi đã đánh dấu Missing Data với giá trị NaN, ta có thể tính toán được số lượng Missing Data trong mỗi cột bằng hàm isnull() kết hợp với hàm sum() như dưới đây:
# example of marking missing values with nan values
from numpy import nan
from pandas import read_csv
# load the dataset
dataset = read_csv( 'pima-indians-diabetes.csv' , header=None)
# replace ' 0 ' values with ' nan '
dataset[[1,2,3,4,5]] = dataset[[1,2,3,4,5]].replace(0, nan)
# count the number of nan values in each column
print(dataset.isnull().sum())
Kết quả thực hiện:
0 0
1 5
2 35
3 227
4 374
5 11
6 0
7 0
8 0
dtype: int64
Ta thấy cột thứ 3 và 4 có rất nhiều Missing Data, trong khi cột thứ 1 và thứ 5 chỉ có một vài Missing Data.
Có rất nhiều cách để xử lý loại bỏ vấn đề Missing Data. Chúng ta sẽ đi sâu, tìm hiểu 4 phương pháp phổ biến nhất.
Trước khi đi vào tìm hiểu các phương pháp đó, chúng ta sẽ thực hiện mô hình hóa một thuật toán với dữ liệu chứa Missing Data để xem kết quả ra sao. Kết quả này, sau đó được đem ra so sánh với kết quả mô hình hóa cùng thuật toán trên dữ liệu đã loại bỏ Missing Data theo từng phương pháp.
Mình chọn mô hình hóa thuật toán LDA sử dụng 3-Fold Cross-validation. Code thực hiện như sau:
# example where missing values cause errors
from numpy import nan
from pandas import read_csv
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
# load the dataset
dataset = read_csv('pima-indians-diabetes.csv' , header=None)
# replace ' 0 ' values with ' nan '
dataset[[1,2,3,4,5]] = dataset[[1,2,3,4,5]].replace(0, nan)
# split dataset into inputs and outputs
values = dataset.values
X = values[:,0:8]
y = values[:,8]
# define the model
model = LinearDiscriminantAnalysis()
# define the model evaluation procedure
cv = KFold(n_splits=3, shuffle=True, random_state=1)
# evaluate the model
result = cross_val_score(model, X, y, cv=cv, scoring= 'accuracy')
# report the mean performance
print('Accuracy: %.3f' % result.mean())
Kết quả thực hiện:
ValueError: Input contains NaN, infinity or a value too large for dtype('float64').
Có lỗi xảy ra đúng như mình đã nói từ đầu, phần lớn các thuật toán ML không thể làm việc được với Missing Data. OK, giờ là lúc chúng ta đi vào tìm hiểu chi tiết từng phương pháp xử lý Missing Data.
Đây là phương pháp đơn giản nhất để loại bỏ Missing Data. Tuy nhiên, nếu số lượng Missing Data quá nhiều mà ta lại xóa hết di thì phần còn lại sẽ không đủ để mô hình hóa dữ liệu đó. Thông thường, nếu tỉ lệ Missing Data nhỏ hơn 5% trên tổng số thì ta nên xóa chúng.
Để xóa hàng/cột chứa Missing Data, ta sẽ sử dụng hàm dropna() của Pandas. Tham số axis=0/1 chỉ ra là xóa hàng/cột, mặc định là xóa hàng. Tham số inplace=True/False chỉ ra việc lưu/không lưu thay đổi vào Dataframe ban đầu, mặc định là False. Code thực hiện như sau:
# example of removing rows that contain missing values
from numpy import nan
from pandas import read_csv
# load the dataset
dataset = read_csv('pima-indians-diabetes.csv' , header=None)
# summarize the shape of the raw data
print(dataset.shape)
# replace ' 0 ' values with ' nan '
dataset[[1,2,3,4,5]] = dataset[[1,2,3,4,5]].replace(0, nan)
# drop rows with missing values
dataset.dropna(inplace=True)
# summarize the shape of the data with missing rows removed
print(dataset.shape)
Kết quả thực hiện:
(768, 9)
(392, 9)
Từ 768 hàng, sau khi xóa các hàng có chứa Missing Data, chỉ còn lại 392 hàng.
Chúng ta sẽ thực hiện lại việc mô hình hóa thuật toán LDA trên tập dữ liệu đã loại bỏ Missing Data:
# evaluate model on data after rows with missing data are removed
from numpy import nan
from pandas import read_csv
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis
from sklearn.model_selection import KFold
from sklearn.model_selection import cross_val_score
# load the dataset
dataset = read_csv('pima-indians-diabetes.csv' , header=None)
# replace ' 0 ' values with ' nan '
dataset[[1,2,3,4,5]] = dataset[[1,2,3,4,5]].replace(0, nan)
# drop rows with missing values
dataset.dropna(inplace=True)
# split dataset into inputs and outputs
values = dataset.values
X = values[:,0:8]
y = values[:,8]
# define the model
model = LinearDiscriminantAnalysis()
# define the model evaluation procedure
cv = KFold(n_splits=3, shuffle=True, random_state=1)
# evaluate the model
result = cross_val_score(model, X, y, cv=cv, scoring= 'accuracy')
# report the mean performance
print( 'Accuracy: %.3f' % result.mean())
Kết quả:
Accuracy: 0.781
Như vậy là chúng ta đã mô hình hóa thành công thuật toán LDA sau khi đã xóa bỏ Missing Data trong bộ dữ liệu của chúng ta.
Bài hôm nay cũng đã khá dài,mình sẽ kết thúc tại đây. Chúng ta đã cùng nhau tìm hiểu về hiện tượng Missing Data là gì? tác hại của nó ra sao? Cách nhận biết và thống kê nó. Chúng ta cũng đã tìm hiểu được một phương pháp đơn giản nhất để loại bỏ Missing Data, đó là xóa các hàng/cột có chứa Missing Data.
Toàn bộ code của bài này, các bạn có thể tham khảo tại đây.
Trong bài tiếp theo, chúng ta sẽ tìm hiểu về phương pháp tiếp theo trong việc xử lý Missing Data, đó là Statisticcal Imputation. Mời các bạn đón đọc.
[1] Jason Brownlee, “Data Preparation for Machine Learning”, Book: https://machinelearningmastery.com/data-preparation-for-machine-learning/.