1 Tổng quan nội dung bài học

Bài học này cung cấp kiến thức về cách xử lý các giá trị ngoại lệ (outliers) và dữ liệu bị thiếu (missing data) khi thực hiện phân tích khám phá dữ liệu (data exploratory analysis - EDA) đối với dữ liệu có cấu trúc. Trong những bộ dữ liệu thực tế thì ta rất hay gặp phải outliers và missing data, và ta cần phải xử lý chúng đúng cách để từ đó có thể phân tích và tìm ra những insight chính xác từ dữ liệu.

2 Hướng dẫn thực hành

Để thực hành viết code theo tài liệu này cũng như để làm các bài tập, sử dụng Google Colab notebook sau đây:

https://colab.research.google.com/drive/1g9dTYOrj5ds7fr_tPLR5uuSqc7vStZH2?usp=sharing

3 Xử lý outliers

3.1 Giá trị ngoại lệ (outlier) là gì?

Trong xác suất thống kê, giá trị ngoại lệ (outlier) là một giá trị mà khác biệt đáng kể so với các quan sát khác trong tập dữ liệu. Thường khi nói đến giá trị ngoại lệ là ta nói đến dữ liệu kiểu số (numerical). Các giá trị ngoại lệ của một trường dữ liệu kiểu số thường là các giá trị mà khác xa so với các giá trị còn lại của trường đó, hoặc là một giá trị mà phi thực tế (ví dụ như tuổi âm hay tỷ lệ phần trăm của một thành phần trong nhóm lại vượt quá 100%, v.v.). Tuy nhiên với dữ liệu kiểu categorical ta cũng có những giá trị ngoại lệ, thường là những giá trị mà có tần suất xuất hiện rất thấp trong trường dữ liệu đó, hoặc là giá trị phi thực tế (ví dụ tên một tỉnh/thành phố mà không tồn tại trong thực tế, v.v.).

3.2 Khi nào thì cần loại bỏ outlier?

Ta loại bỏ outlier khi:

  • Các outlier chắc chắn là do lỗi dữ liệu (lỗi nhập liệu hoặc lỗi hệ thống, v.v.)

  • Các outlier mang tính chất chỉ xuất hiện 1 lần (1 vài sự kiện đặc biệt hoặc 1 vài KH, sản phẩm, v.v. đặc biệt) và không thể hiện thể hiện 1 phần trọng yếu của bản chất hoạt động kinh doanh, vận hành

Ta không loại bỏ outlier khi:

  • Kết quả phân tích mang tính chất báo cáo, cần số liệu chính xác tuyệt đối thay vì tìm ra các pattern và xu hướng

  • Có quá nhiều outliers. VD: nếu có 20% các quan sát được xác định là outliers, thì có nghĩa là có thể có những thông tin đáng giá ở đây, ta nên điều tra thêm thay vì bỏ hết chúng đi.

  • Trong machine learning, không phải lúc nào ta cũng cần loại bỏ outlier vì nếu loại bỏ nguyên 1 record chỉ vì có outlier ở một cột thì ta sẽ mất dữ liệu hữu ích ở các cột khác, hoặc do có các thuật toán mà bản thân thuật toán giúp ta loại bỏ được tác động của outlier.

Nếu không loại bỏ outlier thì có những cách xử lý outlier phổ biến như sau:

  • Với dữ liệu kiểu số (numerical data):

    • Nếu dữ liệu phi thực tế (ví dụ như tuổi là số âm) thì ta có thể thay giá trị đó bằng giá trị trống (NaN, NULL, …). Lúc này, ta có thể xử lý tiếp như ở phần Xử lý dữ liệu bị thiếu.

    • Nếu dữ liệu là một giá trị rất lớn hoặc rất nhỏ, khác xa so với các giá trị còn lại, có khả năng xảy ra nhưng xác suất thấp, ta có thể xử lý bằng cách chặn trên hoặc chặn dưới (clipping hoặc capping). Ta đưa các outlier đó về giá trị lớn nhất/nhỏ nhất mà được coi là bình thường.

  • Với dữ liệu kiểu hạng mục (categorical data)

    • Nếu giá trị ngoại lệ là do lỗi nhập liệu (typo / typing mistake) hoặc do không chuẩn hoá trong cách nhập liệu, ví dụ như “Hanoi” thì nhập thành “Hanooi” hay “hn, … thì ta cần chuẩn hoá các giá trị đó, đưa chúng về cùng 1 giá trị duy nhất trước khi phân tích.

    • Nhóm các giá trị có tần suất xuất hiện rất thấp này lại thành 1 hạng mục và đặt tên là “Other” hoặc “Rare” (hoặc một tên gọi nào đó bạn cho là phù hợp)

    • Trong việc xây dựng các mô hình Machine Learning, ta cũng có thể thực hiện feature engineering (tạo mới hoặc biến đổi các feature/cột sẵn có) để thể hiện tốt hơn thông tin trong các giá trị ngoại lệ. Việc xây dựng các mô hình Machine Learning nằm ngoài phạm vi của khoá học này.

3.3 Xử lý giá trị ngoại lệ với dữ liệu kiểu số (numerical data)

Để xác định các giá trị ngoại lệ (rất lớn hoặc rất nhỏ, xác suất xảy ra thấp), ta có thể áp dụng một số quy tắc thông dụng sau:

  • Quy tắc sử dụng độ lệch chuẩn (standard deviation) với dữ liệu phân phối chuẩn (normal distribution):

    Với các phân phối dạng normal distribution hoặc gần giống như vậy, ta có thể dùng standard deviation (σ) để xác định các outlier:

    Outliers là những giá trị nằm ngoài khoảng median ± 3σ

    Xem ảnh minh hoạ dưới đây

  • Quy tắc IQR (inter quartile range): Để xác định các outlier thì thường có 1 quy tắc dễ nhớ đó là ta sẽ coi các quan sát có giá trị < Q1 - 1.5IQR hoặc > Q3 + 1.5IQR là outlier.

    Nhưng tại sao lại là ±1.5IQR?

    Vì người ta chứng minh được bằng toán rằng với normal distribution, giá trị

    Q1 - 1.5IQR

    = (median - 0.6745σ) - 1.5(Q3 - Q1)

    = (median - 0.6745σ) - 1.5(median + 0.6745σ - (median - 0.6745σ))

    = median - 0.6745σ - 1.5(1.349σ)

    = median - 2.698σ

    Tương tự: Q3 + 1.5IQR = median + 2.698σ

    Vậy ta thấy số 1.5 đóng vai trò là 1 scale parameter (nghĩa là ta có thể chọn 1 giá trị khác 1.5). Với normal distribution khi chọn tham số này = 1.5 thì ta xác định được outlier là các quan sát nằm ngoài khoảng median ± 2.698σ (tương đương với 100% - 99.3% = 0.7% tổng số quan sát), tức là khá gần với median ± 3σ (tức là 100% - 99.73% = 0.27% tổng số quan sát)

    Rule ±1.5IQR áp dụng được với cả normal lẫn skewed distribution, nhưng tỷ lệ % outlier trên tổng số quan sát của skewed distribution sẽ không giống bên trên.

Có những cách khác để xác định outliers, nhưng quy tắc ±1.5xIQR là một quy tắc dễ hiểu và dễ nhớ.

Nếu quan sát có giá trị > Q3 + 3IQR hoặc < Q1 – 3IQR thì nó còn có thể được gọi là một extreme outlier.

Thực hành:

Đề bài: Cho bộ dữ liệu sau:

github_url = 'https://raw.githubusercontent.com/tuanphan92/datatute-datasets/main/data-analytics-booster/EcommerceVisualization.csv'

import pandas as pd
df = pd.read_csv(github_url, encoding='latin1', sep=",")
InvoiceNo StockCode Description Quantity InvoiceDate UnitPrice CustomerID Country
536365 22752 SET 7 BABUSHKA NESTING BOXES 2 12-01-2010 08:26 7.65 17850 United Kingdom
536365 85123A WHITE HANGING HEART T-LIGHT HOLDER 6 12-01-2010 08:26 2.55 17850 United Kingdom
536365 71053 WHITE METAL LANTERN 6 12-01-2010 08:26 3.39 17850 United Kingdom
536365 84029G KNITTED UNION FLAG HOT WATER BOTTLE 6 12-01-2010 08:26 3.39 17850 United Kingdom
536365 84029E RED WOOLLY HOTTIE WHITE HEART. 6 12-01-2010 08:26 3.39 17850 United Kingdom

Hãy phân tích và đưa ra nhận xét về số lượng mặt hàng trong mỗi đơn hàng.

Hướng dẫn các bước thực hiện:

  1. tính toán các quantiles cho cột Quantity và visualise dữ liệu bằng boxplot và histogram để có cái nhìn tổng quan về số lượng mặt hàng trong mỗi đơn hàng và phát hiện các outlier
  2. tìm hiểu lí do vì sao có các outlier đó (trong phạm vi bài học này thì bạn không thể làm được bước này nên bỏ qua, còn trong thực tế thì bạn nên thực hiện bước này)
  3. xử lý các outlier
  4. tính toán lại các quantiles, visualise lại dữ liệu, và đưa ra nhận xét

Đáp án ở trong file google colab notebook thực hành.

3.4 Xử lý giá trị ngoại lệ với dữ liệu kiểu hạng mục (categorical data)

Để xác định các giá trị ngoại lệ với dữ liệu kiểu hạng mục, đầu tiên ta cần nhóm dữ liệu theo từng hạng mục và tính tần suất xuất hiện của từng hạng mục.

Ví dụ:

import pandas as pd
import random

mydata = {
    'transaction_id': list(range(1, 101)),
    'product_name': random.choices(["book", "pencil", "pen", "notebook", "eraser"], k=100)
}
mydf = pd.DataFrame.from_dict(mydata)

# Thêm một số dòng dữ liệu vào dataframe để tạo ra các giá trị ngoại lệ
mydf.loc[len(mydf.index)] = {'transaction_id': 100, 'product_name': 'marker'}
mydf.loc[len(mydf.index)] = {'transaction_id': 101, 'product_name': 'marker'}
mydf.loc[len(mydf.index)] = {'transaction_id': 101, 'product_name': 'board'}

mydf
##      transaction_id product_name
## 0                 1       eraser
## 1                 2         book
## 2                 3       pencil
## 3                 4       eraser
## 4                 5       pencil
## ..              ...          ...
## 98               99     notebook
## 99              100     notebook
## 100             100       marker
## 101             101       marker
## 102             101        board
## 
## [103 rows x 2 columns]
# Tính tần suất xuất hiện của các giá trị trong cột `product_name`
product_frequency_df = mydf.groupby(
  'product_name'
).agg(
  row_count=(
    "transaction_id", 
    "count"
  )
).reset_index()

product_frequency_df["frequency"] = product_frequency_df["row_count"] / product_frequency_df["row_count"].sum()
product_frequency_df
##   product_name  row_count  frequency
## 0        board          1   0.009709
## 1         book         12   0.116505
## 2       eraser         26   0.252427
## 3       marker          2   0.019417
## 4     notebook         20   0.194175
## 5          pen         22   0.213592
## 6       pencil         20   0.194175
# Nếu muốn viết code nhanh gọn hơn thì bạn có thể dùng hàm value_counts như dưới đây, 
# kết quả trả ra vẫn giống bên trên
frequency = mydf['product_name'].value_counts(normalize=True)
print(frequency)
## product_name
## eraser      0.252427
## pen         0.213592
## pencil      0.194175
## notebook    0.194175
## book        0.116505
## marker      0.019417
## board       0.009709
## Name: proportion, dtype: float64

Vậy tần suất xuất hiện bao nhiêu thì coi là hiếm và có thể coi là giá trị ngoại lệ? Câu trả lời là không có một quy tắc cụ thể nào cả mà phải phụ thuộc vào bản chất của tập dữ liệu và công việc phân tích bạn đang làm hay mô hình Machine Learning bạn đang xây dựng. Tuy nhiên, thường nếu tần suất xuất hiện của một giá trị < 5% thì ta có thể coi đó là giá trị ngoại lệ.

Trong trường hợp này, giá trị “marker” và “board” có tần suất xuất hiện lần lượt là ~2% và ~1% nên ta coi đây là các giá trị ngoại lệ.

Ta xử lý bằng cách nhóm chúng lại thành 1 hạng mục gọi là “Other”.

mydf['product_name'] = mydf['product_name'].replace(
  {'marker': 'other', 'board': 'other'}
)

frequency = mydf['product_name'].value_counts(normalize=True)
print(frequency)
## product_name
## eraser      0.252427
## pen         0.213592
## pencil      0.194175
## notebook    0.194175
## book        0.116505
## other       0.029126
## Name: proportion, dtype: float64

4 Xử lý dữ liệu bị thiếu

Với dữ liệu bị thiếu / khuyết, ta thường có các cách xử lý sau đây:

  • Bỏ cả bản ghi có dữ liệu bị thiếu đi. Cách làm này tuy đơn giản nhưng sẽ khiến ta mất dữ liệu của những cột mà không bị thiếu dữ liệu của bản ghi đó (tức là chỉ vì một cột của bản ghi đó thiếu dữ liệu mà bỏ cả bản ghi đi). Trường hợp mà dữ liệu bị thiếu của các cột đều rơi vào một vài bản ghi nhất định (tức là hầu hết các bản ghi trong bảng/dataframe đều có đầy đủ dữ liệu, chỉ riêng một số bản ghi nhất định là thiếu dữ liệu và thiếu ở nhiều cột) thì có thể đây là những bản ghi bị lỗi hoặc có vấn đề gì đó, và nếu số lượng bản ghi lỗi không lớn và số lượng bản ghi còn lại là đủ nhiều để phân tích, xây mô hình ML, … thì ta có thể bỏ chúng đi.

  • Bỏ cả cột dữ liệu đi. Nếu cột dữ liệu bị thiếu quá nhiều (thiếu bao nhiêu là nhiều thì tuỳ vào bản chất của bộ dữ liệu và các nghiệp vụ liên quan) và cột dữ liệu đó không còn đủ tin cậy để sử dụng nữa thì ta có thể bỏ cả cột đi. Trước khi bỏ cả cột đi ta cũng cần cân nhắc xem những cột còn lại có đủ thông tin để ta sử dụng hay không (để phân tích, xây mô hình ML, …).

  • Với dữ liệu dạng số (numerical data), khi xây dựng các mô hình ML, thông thường ta sẽ điền một giá trị nào đó vào các giá trị bị thiếu, thường là số trung vị (median) hoặc trung bình cộng (average). Còn khi làm báo cáo hoặc thực hiện phân tích khám phá tìm kiếm insights, tuỳ vào tính chất của cột dữ liệu mà ta quyết định có cần xử lý dữ liệu bị thiếu hay không. Với việc làm báo cáo, phân tích, thông thường nếu dữ liệu của cột nào đó bị thiếu ít thôi thì ta có thể không cần xử lý, đặc biệt là những cột mà ta cần dùng để tính số tổng (ví dụ giá trị hợp đồng, giá trị giao dịch, … ta không thể tuỳ tiện điền thêm số vào các giá trị bị thiếu được). Nếu cột thiếu nhiều dữ liệu thì tốt nhất ta nên kiểm tra nguyên nhân và thực hiện các giải pháp để có thể lấy được đủ và chính xác dữ liệu, hoặc sử dụng những cột khác (ví dụ cột tổng số tiền bị thiếu nhiều thì ta tự tính bằng cách lấy đơn giá nhân với số lượng sản phẩm, hoặc tìm và tổng hợp dữ liệu từ một nguồn khác đáng tin cậy hơn).

  • Với dữ liệu hạng mục (categorical data), khi làm báo cáo hoặc phân tích khám phá, thường ta có thể thay các giá trị bị thiếu (NULL, NaN, NA, …) bằng một hạng mục mới, thường gọi là “Unknown” hoặc “Missing”, … còn khi xây dựng các mô hình ML, thường ta có thể xử lý bằng cách điền giá trị mà xuất hiện nhiều nhất (mode) vào các giá trị bị thiếu, hoặc cũng có thể thay các giá trị bị thiếu bằng hạng mục “Unknown” / “Missing” như nêu trên.