處理資料缺失值的方法 — 平均數與中位數插值法及Python實作

Shinyui Chu
數據分析不是個事
9 min readSep 28, 2020

在上一篇我們有提到如何利用完整資料分析(CCA)來處理資料缺失的問題,我們知道完整資料分析(CCA)的使用前提是資料的缺失是隨機的(MCAR),而且缺失比例不能大於5%。但是除了完整資料分析外我們還有平均數與中位數插值法(Mean/Median Imputation)可以利用,在這邊我們會介紹這兩種差值法的假設、優缺點及Python實作。

什麼是插值法(Imputation)?

插值法的概念其實很簡單,就是把缺失的數值用某個數字補起來。

那至於這個數字是什麼?可以是由某個公式或是你自己決定要插入什麼數字!

平均數與中位數插值法是什麼?

在上一段我們知道插值法的概念就是把缺失的值用某個數字補上去,而平均數與中位數插值法是其中最常用的方法之一。從名字應該可以看出平均數與中位數插值法就是把缺失值用平均數或中位數給補起來。

那什麼時候該用平均數跟中位數呢?我們可以從資料的我們可以從資料的偏態來了解~

我們可以看到當資料是常態分佈時我們可以利用平均數跟中位數因為這兩個數字基本上差異不大,但是當資料呈現負偏態的時候用中位數會更貼近現實,因為平均數會被離群值影響,相對的當資料呈現正偏態的時候使用中位數也是一個較好的選擇。

平均數與中位數插值法的假設及適用情況

平均數與中位數插值法的假設是資料的缺失情況必須是Missing Completely at Random的情況下才能使用平均數與中位數插值法,同時這個方法的假設是缺失的資料會跟多數資料長得差不多(平均數或是中位數)。

使用這個方法的好處是能夠快速地被實踐出來,但是這個方法會破壞(Distort)原始資料的分布狀況(Distribution)、變異數(Variance)還有與其他變數的共變異數(Covariance)。

比例的話跟完整資料分析相同,大約是5%的缺失資料會可以適用平均數與中位數插值法。缺失比例越高,採用這個方法會使得資料分布狀況(Distribution)、變異數(Variance)還有與其他變數的共變異數(Covariance)被破壞的程度越嚴重。

這邊要注意的是平均數與中位數插值法,只能在訓練集上做計算,然後再將結果套用在測試集上。換句或說,我們要先針對原始資料做 train_test_split 然後把訓練集的資料作平均數或是中位數的計算(也就是fit),最後把結果transform 在訓練集跟測試集上。

這邊我們用簡單的程式碼來作範例

import pandas as pd
from sklearn.impute import SimpleImputer
from sklearn.model_selection import train_test_split
df = pd.read_csv("your_csv_file.csv")X = df.drop("target_variable", axis=1)
y = df["target_variable]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=42)
imp = SimpleImputer(missing_values=np.nan, strategy='mean')
imp.fit(X_train)
X_train = imp.transform(X_train)
X_test = imp.transform(X_test)

這樣一來就可以防止資料洩漏(Data Leakage)所造成的模型過擬合(Over-fitting),關於這個問題的深度解釋我們之後會寫一篇文章來解釋什麼叫做資料洩漏、為什麼會造成過擬合以及如何預防~~

平均數與中位數插值法Python實作

接下來我們要談談如何在Pytho實作平均數與中位數插值法,跟上一篇文章一樣我們使用的是Kaggle的House Prices: Advanced Regression Techniques這個比賽的訓練資料集。

先前已經有提過如何篩選出缺失值的資料,這邊就不再贅述了。基本上我們可以利用以下程式碼將有缺失資料的變數以缺失的比例給計算出來。

df.isnull().mean()

在這個範例中我們會利用"MasVnrArea", "GarageYrBlt"還有"LotFrontage"這三個變數來示範,我們先來看一下這三個變數的缺失資料比例有多少,我們可以利用這個程式碼來達成。

df = df[["MasVnrArea", "GarageYrBlt", "LotFrontage", "SalePrice"]]
df.isnull().mean()*100

結果如下

我們可以看到這3個變數的缺失比例從0.5% ~ 17.7%都有,挑選這3個變數的主要原因是稍後想讓大家了解不同缺失比例下的變異程度有多少。而 SalesPrice則是我們要預測的數字,保留它是因為我們稍後要做train_test_split。

我們接著來把資料做拆分,我們可以利用下面這行程式碼來達成

from sklearn.model_selection import train_test_splitX = df.drop(“SalePrice”, axis=1)
y = df[“SalePrice”]
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.3, random_state=42)

接著來做中位數插值法,採用這個方法的原因是資料不是常態分布,我們可以用以下程式碼畫個簡單的圖表作分析

f, ax = plt.subplots(nrows=1, ncols=3, figsize=(20, 5))
ax = ax.flatten()
sns.distplot(X_train[“MasVnrArea”], ax=ax[0])
sns.distplot(X_train[“GarageYrBlt”], ax=ax[1])
sns.distplot(X_train[“LotFrontage”], ax=ax[2])

輸出會長這樣,蠻明顯不是常態分佈,所以採用中位數插值法

接著來插數字,程式碼會長下面這樣

import numpy as np
from sklearn.impute import SimpleImputer
imp = SimpleImputer(missing_values=np.nan, strategy=’median’)imp.fit(X_train)
X_train_transformed = imp.transform(X_train)
X_train_transformed = pd.DataFrame(X_train_transformed, columns=df.columns[0: -1])

在最後一行我們把transformed完成的資料轉變成dataframe是為了後續作圖方便,因為transform完的資料會回傳array而不是dataframe。

接著我們先從變異數來比較其後的差異,程式碼如下

for col in X_train.columns:
print(f"col {col}'s variance is {np.var(X_train[col])} before median imputation")
print(f"col {col}'s variance is {np.var(X_train_transformed[col])} after median imputation")
print(f"difference is {abs(np.var(X_train_transformed[col]) - np.var(X_train[col]))}\n")

輸出結果

我們可以看到,即便只有缺失0.5%的資料,經過中位數插值法後變異數差異也會達到55。

接著來看圖,程式碼如下

f, ax = plt.subplots(nrows=1, ncols=3, figsize=(20, 5))
ax = ax.flatten()
sns.distplot(X_train["MasVnrArea"], ax=ax[0], label="before imputation")
sns.distplot(X_train_transformed["MasVnrArea"], ax=ax[0], label="after imputation")
sns.distplot(X_train["GarageYrBlt"], ax=ax[1], label="before imputation")
sns.distplot(X_train_transformed["GarageYrBlt"], ax=ax[1], label="after imputation")
sns.distplot(X_train["LotFrontage"], ax=ax[2], label="before imputation")
sns.distplot(X_train_transformed["LotFrontage"], ax=ax[2], label="after imputation")
plt.legend();

結果如下

我們可以看到,使用這個插值法的前後,會對資料的分布狀況造成一定影響的,尤其是缺失值比例越大的時候越嚴重!

你喜歡嗎?

喜歡的話可以拍個手👏或是留言讓我知道嗎?如果不喜歡就……算了,我還是會繼續寫😂。

--

--

Shinyui Chu
數據分析不是個事

蠻普通的一個人,20 歲休學,到處看看到處亂撞