處理資料缺失值的方法 — 完整資料分析(Complete Case Analysis)及Python實作
在資料分析(Data Analytics)或是機器學習(Machine Learning)專案的生命週期裡裡的資料清洗(Data Cleaning)絕對是不可或缺的一部分。而其中懂得處理資料缺失值(Missing Data)絕對是專案成功的一個關鍵!處理資料缺失值的方法有很多種,在這篇文章中我們會介紹其中最常見的一種方法 — 完整資料分析(Complete Case Analysis)以及背後的Python實作!
在今天寫這篇文章時,閱讀到這篇文章 Untold Truths of being a Machine Learning Engineer,當中提到了機器學習工程師會花多數的時間把資料轉換成能反應出實際狀況的資料。
ML engineers spend most of the time working on “how to properly extract the training set that will resemble real-world problem distribution”
在多數的情況下,當我們能確實做好資料的預處理時,即便用一般的機器學習模型也能達成還不錯的效果。
而這篇文章也再次說明了資料預處理的重要性!
什麼是完整資料分析(Complete Case Analysis)?
完整資料分析(CCA)的概念看起來好像很複雜,但是在Python上的實踐我們可以利用Pandas的一個方法就能達到,而且我打賭你一定看過這個方法!
df = df["tar_variable"].dropna()
是不是很熟悉?沒錯,如果我們"Pandas"的一點來說,完整資料分析其實就只是把缺失的資料刪除掉而已!
這麼簡單?沒錯就是這麼簡單?簡單的東西我們沒必要把它變得複雜嘛!對吧(當然裝B除外🤣)?
完整資料分析假設及適用情況
在完整資料分析中的假設是資料的缺失的模式(Pattern)必須是隨機的,換句缺失資料的是隨機(Random)的也就是MCAR(Missing Completely at Random),不能有某種的模式存在。
當然不是每種符合MCAR的變數都能可以使用CCA,假如缺失資料的比重占太多,直接採用CCA可能會破壞(Distort)那個變數的資料分布狀態(Data Distirbution)。所以缺失值的比例不能太高!
那缺失值的比例超過多少就算高呢?10%?15%?還是25%?
其實只要缺失值的比例只要超過5%就不建議採用CCA,換言之只要5%以下而且資料的缺失是隨機的就能使用CCA喔~~
完整資料分析Python實作
了解完理論後,接著我們要來學習如何利用Python來做CCA。我們使用的資料集是著名的Kaggle House Prices: Advanced Regression Techniques競賽,資料可以透過上面的連結取得。順帶一提,這場競賽非常適合新手作為第一場的機器學習競賽,如果能打進全球前5%就很不錯惹~~
喔對了,我們是拿訓練集來demo而不是測試集喔!
首先我們import 一些必要的模塊還有利用Pandas讀取資料集,程式碼看起來會像是這樣。
import pandas as pd
import numpy as np
import matplotlib.pyplot as pltdata = pd.read_csv(“train.csv”)
data.head()
輸出結果看起來會是長這樣
接下來,我們要找出那些自變數(或是特徵)是有缺值的,程式碼看起來會像是這樣。
var_na = [col for col in df.columns if df[col].isnull().mean() > 0]
輸出結果看起來會是長這樣
['LotFrontage',
'Alley',
'MasVnrType',
'MasVnrArea',
'BsmtQual',
'BsmtCond',
'BsmtExposure',
'BsmtFinType1',
'BsmtFinType2',
'Electrical',
'FireplaceQu',
'GarageType',
'GarageYrBlt',
'GarageFinish',
'GarageQual',
'GarageCond',
'PoolQC',
'Fence',
'MiscFeature']
你可能有注意到我篩選出缺值的變數所採用的方法是 mean 而不是用 sum,原因是因為我們想要得知缺值占該變數的比例,而mean可以做到這點。當然我們也是可以用缺值的數量除以全部的數量,不過就有點多此一舉。
還記得我們在上一段有提到缺值要小於5%才適用CCA嗎?接著我們要來篩選出小於5%的變數,具體而言我們可以利用以下程式碼達成。
cca_var = [var for var in df.columns if df[var].isnull().mean() < 0.05]
輸出結果則是
['Id',
'MSSubClass',
'MSZoning',
'LotArea',
'Street',
'LotShape',
'LandContour',
'Utilities',
'LotConfig',
'LandSlope',
'Neighborhood',
'Condition1',
'Condition2',
'BldgType',
'HouseStyle',
'OverallQual',
'OverallCond',
'YearBuilt',
'YearRemodAdd',
'RoofStyle',
'RoofMatl',
'Exterior1st',
'Exterior2nd',
'MasVnrType',
'MasVnrArea',
'ExterQual',
'ExterCond',
'Foundation',
'BsmtQual',
'BsmtCond',
'BsmtExposure',
'BsmtFinType1',
'BsmtFinSF1',
'BsmtFinType2',
'BsmtFinSF2',
'BsmtUnfSF',
'TotalBsmtSF',
'Heating',
'HeatingQC',
'CentralAir',
'Electrical',
'1stFlrSF',
'2ndFlrSF',
'LowQualFinSF',
'GrLivArea',
'BsmtFullBath',
'BsmtHalfBath',
'FullBath',
'HalfBath',
'BedroomAbvGr',
'KitchenAbvGr',
'KitchenQual',
'TotRmsAbvGrd',
'Functional',
'Fireplaces',
'GarageCars',
'GarageArea',
'PavedDrive',
'WoodDeckSF',
'OpenPorchSF',
'EnclosedPorch',
'3SsnPorch',
'ScreenPorch',
'PoolArea',
'MiscVal',
'MoSold',
'YrSold',
'SaleType',
'SaleCondition',
'SalePrice']
接著我們要把上面這些變數的缺失值全部drop掉,程式碼會長這樣
cca_df = df[cca_var].dropna(
接著看一下前後總共刪掉了多少資料
cca_df.shape[0], df.shape[0]
執行結果
(1412, 1460)
我們總共drop掉了48筆資料
接下來,我們來看一下CCA後的資料變動,我們拿GrLivArea這個變數為例。我們可以利用下面這段程式碼來看後續變異的狀況。
plt.hist(df[“GrLivArea”], bins=50, color=”red”, label=”Original”)
plt.hist(cca_df[“GrLivArea”], bins=50, color=”blue”, alpha=0.7, label=”After”)
plt.legend();
輸出結果會長這樣
我們可以看到整體資料的分布並沒有變太多,這也是為什麼我們要設0.05的原因,如果設太高譬如是…… 0.65好了,資料看起來會像是這樣。
相較上一張圖,我們可以看到資料有了很劇烈的改動。
你喜歡嗎?
喜歡的話可以拍個手👏或是留言讓我知道嗎?如果不喜歡就……算了,我還是會繼續寫😂。