Hướng dẫn dùng python dropna python

Khi mới bắt đầu làm quen với việc xây dựng Machine Learning Model với Python, chúng ta thường sử dụng dữ liệu mẫu từ các trang như UCI, Kaggle và TensorFlow Dataset. Đa phần dữ liệu mẫu có chất lượng rất tốt về mọi mặt từ sự cân bằng (Balance) cho đến tính toàn vẹn (Completeness) và sự nhất quán (Consistency). Tuy nhiên trong thực tế, dữ liệu không hoàn toàn đạt được chất lượng cao như vậy, đôi khi một giá trị trong cột không chính xác vì lỗi đánh máy, hoặc có khi không cân bằng giữa các label với nhau hay thậm chí phổ biến hơn là thiếu sót dữ liệu (Missing data). Trong bài viết này, mình sẽ hướng dẫn các bạn quy trình căn bản và các kỹ thuật để xử lý các ô dữ liệu trống trong bảng bằng Python.

Chuẩn bị

Tương tự như những bài viết khác, đầu tiên chúng ta sẽ chuẩn bị các gói thư viện cần thiết và đọc dữ liệu vào. Các bạn có thể tham khảo bài viết này nếu muốn tìm hiểu các thủ thuật đọc dữ liệu CSV: https://datasciencevn.com/python/144-cac-thu-thuat-doc-du-lieu-csv-vao-pandas-data-frame.html

Trước hết các bạn cài đặt pandas nếu chưa từng cài trên môi trường Python nội bộ.

!pip install pandas

Sau đó bạn khai báo thư viện này trong Notebook bằng đoạn code sau (gói thư viện os được dùng cho truy xuất file và tương tác với File System).

import pandas as pd
import numpy as np
import os

Tiếp theo bạn kiểm tra và thiết lập một biến WORKING_DIRECTORY để lưu vị trí hiện tại của chương trình và liệt kê các file có trong directory hiện tại.

WORKING_DIRECTORY = os.getcwd()
print(WORKING_DIRECTORY)
>> /tf/notebooks/missing_data
os.listdir(WORKING_DIRECTORY)
>> ['.ipynb_checkpoints', 'Notebook.ipynb', 'data.csv']

Giờ chúng ta sẽ đọc dữ liệu từ file data.csv vào.

DATA_FILE = f'{WORKING_DIRECTORY}/data.csv'
df = pd.read_csv(DATA_FILE)
df.head()

Hướng dẫn dùng python dropna python

Kiểm tra dữ liệu trống

Đầu tiên chúng ta cần xác định số lượng ô dữ liệu trống trong bảng.

df.info()

RangeIndex: 100 entries, 0 to 99
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   V1      97 non-null     object 
 1   V2      95 non-null     float64
 2   V3      96 non-null     float64
 3   V4      94 non-null     float64
dtypes: float64(3), object(1)
memory usage: 3.2+ KB

Với hàm info(), chúng ta có thể nhanh chóng kiểm tra thông tin sơ bộ về bảng dữ liệu này. Tổng cộng bảng dữ liệu có 100 dòng, trong đó cột V1 có 97 ô “không trống”, nghĩa là có 3 ô trống trong cột này. Tương tự, cột V2, V3 và V4 có lần lượt là 5, 4 và 6 ô trống.

Để kiểm tra riêng lẻ cho từng cột, các bạn có thể dùng hàm isna() như sau.

pd.isna(df['V4'])
0      True
1     False
2     False
3     False
4     False
      ...  
95    False
96    False
97    False
98    False
99    False
Name: V4, Length: 100, dtype: bool

Hàm này sẽ kiểm tra từng ô một và trả về True nếu ô đó là NaN, None hoặc NaT (Datetime). Để đếm số ô trống trong cột này, bạn có thể dùng dòng code sau.

list(pd.isna(df['V4'])).count(True)
>> 6
Như vậy tổng cộng có 6 ô trống trong cột V4, các bạn có thể thực hiện tương tự cho các cột còn lại. Trong trường hợp bạn muốn kiểm tra tất cả các cột cùng một lúc, bạn có thể chạy vòng lặp cho từng cột như sau (Sử dụng Dictionary hoặc List Comprehension)
{col: list(pd.isna(df[col])).count(True) for col in df.columns}
>> {'V1': 3, 'V2': 5, 'V3': 4, 'V4': 6}

Bây giờ chúng ta muốn xem chi tiết hơn về các dòng có chứa ô trống. Chẳng hạn như liệt kê các dòng trong bảng mà ô V1 bị trống.

df[pd.isna(df['V1])]

Hướng dẫn dùng python dropna python

Như vậy dòng số 10, 66 và 87 là những dòng mà ô V1 bị trống. Tương tự chúng ta có thể kiểm tra V3.

df[pd.isna(df['V3'])]

Hướng dẫn dùng python dropna python

Các thủ thuật xử lý ô dữ liệu trống

Về căn bản, có hai cách để các bạn xử lý dữ liệu trống trong bảng. Một là các bạn có thể loại bỏ hoàn toàn dòng có chứa ô trống nếu như số lượng dòng bị loại bỏ không đáng kể so với tổng lượng dữ liệu mà các bạn có. Hai là điền vào ô trống với một giá trị nào đó, chẳng hạn như Mean, Median, hoặc một giá trị cố định. Với dữ liệu kiểu TimeSeries, các thủ thuật phổ biến hơn là dùng giá trị ô liền kề (backfill, forwardfill), giá trị trung bình trong khoảng thời gian nào đó (rolling average), hoặc giá trị trung bình của hai ô gần nhất (interpolation). Trong bài viết này, chúng ta sẽ tạm bỏ qua các thủ thuật liên quan đến TimeSeries vì đây là một chủ đề chuyên sâu hơn mà chúng ta sẽ bàn đến trong một bài viết khác. Một số kỹ thuật cao cấp hơn sử dụng Regression để suy ra giá trị ô trống từ giá trị của các dòng khác nhưng chúng ta cũng sẽ dành riêng chủ đề này trong một bài viết khác.

Loại bỏ dòng chứa ô trống

Đầu tiên chúng ta cần đếm xem có bao nhiêu dòng chứa đầy đủ dữ liệu.

list(pd.isna(df).any(axis=1)).count(False)
>> 83

Như vậy có 83 dòng chứa đầy đủ dữ liệu (không có ô trống nào). Nếu chọn phương thức loại bỏ, tỷ lệ sẽ là 17/100 (17%) . Trong thực tế bạn chỉ nên loại bỏ nếu số lượng dòng có ô trống chiếm chưa đến 1% tổng lượng dữ liệu. Tuy nhiên, mình cũng sẽ hướng dẫn các bạn cách loại bỏ các dòng chứa ô trống trong trường hợp bạn cần phải làm vậy.

Để trích xuất những dòng chứa đầy đủ dữ liệu, các bạn dùng đoạn code sau.

complete_df = df[~pd.isna(df).any(axis=1)].reset_index(drop=True)
complete_df.head()

Hướng dẫn dùng python dropna python

complete_df.info()

RangeIndex: 83 entries, 0 to 82
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   V1      83 non-null     object 
 1   V2      83 non-null     float64
 2   V3      83 non-null     float64
 3   V4      83 non-null     float64
dtypes: float64(3), object(1)
memory usage: 2.7+ KB

Sau khi trích xuất, DataFrame này có 83 dòng và không có ô nào bị trống.

Nếu bạn tò mò muốn trích xuất riêng nhưng dòng không chứa đầy đủ để kiểm tra, thì chỉ cần đảo ngược điều kiện filter.

incomplete_df = df[pd.isna(df).any(axis=1)].reset_index(drop=True)
incomplete_df

Hướng dẫn dùng python dropna python

Đây là 17 dòng có ít nhất một ô trống. Tương tự bạn có thể kiểm tra thêm bằng hàm info().

incomplete_df.info()

RangeIndex: 17 entries, 0 to 16
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   V1      14 non-null     object 
 1   V2      12 non-null     float64
 2   V3      13 non-null     float64
 3   V4      11 non-null     float64
dtypes: float64(3), object(1)
memory usage: 672.0+ bytes

Điền vào ô trống

Để điền vào ô trống với một giá trị cố định nào đó, bạn có thể dùng hàm fillna(). Đầu tiên bạn phải khai báo các giá trị muốn điền vào mỗi cột.

treatment = {
    'V1': 'Unknown',
    'V2': 0.0,
    'V3': round(np.nanmean(df['V3']), 2),
    'V4': round(np.nanmedian(df['V4']), 2)
}

Với cột đầu tiên chứa họ tên, chúng ta sẽ thay các ô trống bằng từ ‘Unknown’. Các ô trống trong cột thứ hai sẽ được thay bằng giá trị 0.0.

Đôi với cột V3, các ô trống sẽ được thay thế bằng giá trị trung bình (Mean) của toàn cột. Hàm nanmean() trong trường hợp này sẽ bỏ qua các ô trống trong công thức tính trung bình. Tương tự với cột V4, chúng ta sử dụng nanmedian, tức giá trị Median bỏ qua các ô trống. Dưới đây là giá trị cụ thể được thay thế vào từng cột.

treatment
>> {'V1': 'Unknown', 'V2': 0.0, 'V3': 10.49, 'V4': 57.08}

Bây giờ chúng ta sẽ sử dụng các giá trị trên đên thay thế ô trống trong bảng dữ liệu. Các bạn hãy để ý dòng đầu tiên (0) và dòng số 3 của bảng trước và sau khi thay thế.

Hướng dẫn dùng python dropna python

treated_df = df.fillna(value=treatment)
treated_df.head(10)

Hướng dẫn dùng python dropna python

Với dòng đầu tiên, giá trị NaN tại V4 đã được thay bằng 57.08 đúng như chúng ta khai báo từ trước. Dòng số 3, giá trị NaN tại V2 và V3 được thay thế lần lượt bằng 0.0 và 10.49.

Kiểm tra lại bằng hàm info().

treated_df.info()

RangeIndex: 100 entries, 0 to 99
Data columns (total 4 columns):
 #   Column  Non-Null Count  Dtype  
---  ------  --------------  -----  
 0   V1      100 non-null    object 
 1   V2      100 non-null    float64
 2   V3      100 non-null    float64
 3   V4      100 non-null    float64
dtypes: float64(3), object(1)
memory usage: 3.2+ KB

Các bạn sẽ thấy hiện tại không còn ô trống nào trong bảng. Như vậy chúng ta đã hoàn tất xử lý các ô dữ liệu trống và sẵn sàng dùng dữ liệu này để train model.