Machine Learning Data Preparation

DP4ML - Missing Data - Phần 1 - Giới thiệu chung

DP4ML - Missing Data - Phần 1 - Giới thiệu chung

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.

1. Missing Data là gì?

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.

2. Nhận diện và đánh dấu Missing Data

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.

3. Các phương pháp xử lý 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.

3.1 Xóa các hàng/cột chứa 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.

3. Kết luận

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.

4. Tham khảo

[1] Jason Brownlee, “Data Preparation for Machine Learning”, Book: https://machinelearningmastery.com/data-preparation-for-machine-learning/.