Python資料分析基礎: 資料缺失值處理

Python資料科學發表於2020-10-31

作者:東哥起飛

公眾號:Python資料科學


聖人曾說過:資料和特徵決定了機器學習的上限,而模型和演算法只是逼近這個上限而已。

再好的模型,如果沒有好的資料和特徵質量,那訓練出來的效果也不會有所提高。資料質量對於資料分析而言是至關重要的,有時候它的意義會在某種程度上會勝過模型演算法。

本篇開始分享如何使用Python進行資料分析,主要側重介紹一些分析的方法和技巧,而對於pandas和numpy等Pyhon計算包的使用會在問題中提及,但不詳細介紹。本篇我們來說說面對資料的缺失值,我們該如何處理。文末有博主總結的思維導圖。

1 資料缺失的原因

首先我們應該知道:資料為什麼缺失?資料的缺失是我們無法避免的,可能的原因有很多種,博主總結有以下三大類:

  • 無意的:資訊被遺漏,比如由於工作人員的疏忽,忘記而缺失;或者由於資料採集器等故障等原因造成的缺失,比如系統實時性要求較高的時候,機器來不及判斷和決策而造成缺失;
  • 有意的:有些資料集在特徵描述中會規定將缺失值也作為一種特徵值,這時候缺失值就可以看作是一種特殊的特徵值;
  • 不存在:有些特徵屬性根本就是不存在的,比如一個未婚者的配偶名字就沒法填寫,再如一個孩子的收入狀況也無法填寫;

總而言之,對於造成缺失值的原因,我們需要明確:是因為疏忽或遺漏無意而造成的,還是說故意造成的,或者說根本不存在。只有知道了它的來源,我們才能對症下藥,做相應的處理。

2 資料缺失的型別

在對缺失資料進行處理前,瞭解資料缺失的機制和形式是十分必要的。將資料集中不含缺失值的變數稱為完全變數,資料集中含有缺失值的變數稱為不完全變數。而從缺失的分佈來將缺失可以分為完全隨機缺失,隨機缺失和完全非隨機缺失。

  • 完全隨機缺失(missing completely at random,MCAR):指的是資料的缺失是完全隨機的,不依賴於任何不完全變數或完全變數,不影響樣本的無偏性,如家庭地址缺失;
  • 隨機缺失(missing at random,MAR):指的是資料的缺失不是完全隨機的,即該類資料的缺失依賴於其他完全變數,如財務資料缺失情況與企業的大小有關;
  • 非隨機缺失(missing not at random,MNAR):指的是資料的缺失與不完全變數自身的取值有關,如高收入人群不原意提供家庭收入;

對於隨機缺失和非隨機缺失,直接刪除記錄是不合適的,原因上面已經給出。隨機缺失可以通過已知變數對缺失值進行估計,而非隨機缺失的非隨機性還沒有很好的解決辦法。

3 資料缺失的處理方法

重點來了,對於各種型別資料的缺失,我們到底要如何處理呢?以下是處理缺失值的四種方法:刪除記錄,資料填補,和不處理

1. 刪除記錄

優點:

  • 最簡單粗暴;

缺點:

  • 犧牲了大量的資料,通過減少歷史資料換取完整的資訊,這樣可能丟失了很多隱藏的重要資訊;
  • 當缺失資料比例較大時,特別是缺失資料非隨機分佈時,直接刪除可能會導致資料發生偏離,比如原本的正態分佈變為非正太;

這種方法在樣本資料量十分大且缺失值不多的情況下非常有效,但如果樣本量本身不大且缺失也不少,那麼不建議使用。

Python中的使用:
可以使用 pandasdropna 來直接刪除有缺失值的特徵。

#刪除資料表中含有空值的行
df.dropna(how='any')

2. 資料填補

對缺失值的插補大體可分為兩種:替換缺失值,擬合缺失值,虛擬變數。替換是通過資料中非缺失資料的相似性來填補,其核心思想是發現相同群體的共同特徵,擬合是通過其他特徵建模來填補,虛擬變數是衍生的新變數代替缺失值。

替換缺失值

  • 均值插補:

對於定類資料:使用 眾數(mode)填補,比如一個學校的男生和女生的數量,男生500人,女生50人,那麼對於其餘的缺失值我們會用人數較多的男生來填補。

對於定量(定比)資料:使用平均數(mean)或中位數(median)填補,比如一個班級學生的身高特徵,對於一些同學缺失的身高值就可以使用全班同學身高的平均值或中位數來填補。一般如果特徵分佈為正太分佈時,使用平均值效果比較好,而當分佈由於異常值存在而不是正太分佈的情況下,使用中位數效果比較好。

注:此方法雖然簡單,但是不夠精準,可能會引入噪聲,或者會改變特徵原有的分佈。
下圖左為填補前的特徵分佈,圖右為填補後的分佈,明顯發生了畸變。因此,如果缺失值是隨機性的,那麼用平均值比較適合保證無偏,否則會改變原分佈。

Python中的使用:
#使用price均值對NA進行填充
df['price'].fillna(df['price'].mean())
df['price'].fillna(df['price'].median())
  • 熱卡填補(Hot deck imputation):

熱卡填充法是在完整資料中找到一個與它最相似的物件,然後用這個相似物件的值來進行填充。通常會找到超出一個的相似物件,在所有匹配物件中沒有最好的,而是從中隨機的挑選一個作為填充值。這個問題關鍵是不同的問題可能會選用不同的標準來對相似進行判定,以及如何制定這個判定標準。該方法概念上很簡單,且利用了資料間的關係來進行空值估計,但缺點在於難以定義相似標準,主觀因素較多。

  • K最近距離鄰法(K-means clustering)

另外一種方法就是利用無監督機器學習的聚類方法。通過K均值的聚類方法將所有樣本進行聚類劃分,然後再通過劃分的種類的均值對各自類中的缺失值進行填補。歸其本質還是通過找相似來填補缺失值。

注:缺失值填補的準確性就要看聚類結果的好壞了,而聚類結果的可變性很大,通常與初始選擇點有關,並且在下圖中可看到單獨的每一類中特徵值也有很大的差別,因此使用時要慎重。

擬合缺失值

擬合就是利用其它變數做模型的輸入進行缺失變數的預測,與我們正常建模的方法一樣,只是目標變數變為了缺失值。

注:如果其它特徵變數與缺失變數無關,則預測的結果毫無意義。如果預測結果相當準確,則又說明這個變數完全沒有必要進行預測,因為這必然是與特徵變數間存在重複資訊。一般情況下,會介於兩者之間效果為最好,若強行填補缺失值之後引入了自相關,這會給後續分析造成障礙。

利用模型預測缺失變數的方法有很多,這裡僅簡單介紹幾種。

  • 迴歸預測:

如我們之前提到的房價預測專案一樣資料分析實戰—北京二手房房價分析(建模篇),基於完整的資料集,建立迴歸方程。對於有缺失值的特徵值,將已知特徵值代入模型來估計未知特徵值,以此估計值來進行填充,以下圖為例。當然關於迴歸的方法有很多,這裡就不詳細介紹了。

缺失值是連續的,即定量的型別,才可以使用迴歸來預測。

  • 極大似然估計(Maximum likelyhood):

在缺失型別為隨機缺失的條件下,假設模型對於完整的樣本是正確的,那麼通過觀測資料的邊際分佈可以對未知引數進行極大似然估計(Little and Rubin)。這種方法也被稱為忽略缺失值的極大似然估計,對於極大似然的引數估計實際中常採用的計算方法是期望值最大化(Expectation Maximization,EM)。該方法比刪除個案和單值插補更有吸引力,它一個重要前提:適用於大樣本。有效樣本的數量足夠以保證ML估計值是漸近無偏的並服從正態分佈。但是這種方法可能會陷入區域性極值,收斂速度也不是很快,並且計算很複雜,且僅限於線性模型。

  • 多重插補(Mutiple imputation):

多值插補的思想來源於貝葉斯估計,認為待插補的值是隨機的,它的值來自於已觀測到的值。具體實踐上通常是估計出待插補的值,然後再加上不同的噪聲,形成多組可選插補值。根據某種選擇依據,選取最合適的插補值。

我們看到,以上提出的擬合和替換方法都是單一的插補方法,而多重插補彌補了單一插補的缺陷,它並沒有試圖去通過模擬值去估計每個缺失值,而是提出缺失資料值的一個隨即樣本(這些樣本可以是不同的模型擬合結果的組合)。這種程式的實施恰當地反映了由於缺失值引起的不確定性,使得統計推斷有效。多重插補推斷可以分為以下3個步驟:

  • 為每個缺失值產生一套可能的插補值,這些值反映了無響應模型的不確定性;
  • 每個插補資料集合都用針對完整資料集的統計方法進行統計分析;
  • 對來自各個插補資料集的結果,根據評分函式進行選擇,產生最終的插補值;

根據資料缺失機制、模式以及變數型別,可分別採用迴歸、預測均數匹配( predictive mean matching, PMM )、趨勢得分( propensity score, PS )、Logistic迴歸、判別分析以及馬爾可夫鏈蒙特卡羅( Markov Chain Monte Carlo, MCMC) 等不同的方法進行填補。

假設一組資料,包括三個變數Y1,Y2,Y3,它們的聯合分佈為正態分佈,將這組資料處理成三組,A組保持原始資料,B組僅缺失Y3,C組缺失Y1和Y2。在多值插補時,對A組將不進行任何處理,對B組產生Y3的一組估計值(作Y3關於Y1,Y2的迴歸),對C組作產生Y1和Y2的一組成對估計值(作Y1,Y2關於Y3的迴歸)。

當用多值插補時,對A組將不進行處理,對B、C組將完整的樣本隨機抽取形成為m組(m為可選擇的m組插補值),每組個案數只要能夠有效估計引數就可以了。對存在缺失值的屬性的分佈作出估計,然後基於這m組觀測值,對於這m組樣本分別產生關於引數的m組估計值,給出相應的預測,這時採用的估計方法為極大似然法,在計算機中具體的實現演算法為期望最大化法(EM)。對B組估計出一組Y3的值,對C將利用Y1,Y2,Y3它們的聯合分佈為正態分佈這一前提,估計出一組(Y1,Y2)。

上例中假定了Y1,Y2,Y3的聯合分佈為正態分佈。這個假設是人為的,但是已經通過驗證(Graham和Schafer於1999),非正態聯合分佈的變數,在這個假定下仍然可以估計到很接近真實值的結果。

注:使用多重插補要求資料缺失值為隨機性缺失,一般重複次數20-50次精準度很高,但是計算也很複雜,需要大量計算。

  • 隨機森林:

另一種比較常用的擬合方法就是隨機森林,這也是Kaggle競賽中大佬們經常使用的一個辦法,具體實現方式與正常一樣,只是將缺失值作為目標變數即可。以下**知識星球專案(一)**中一段程式碼,僅供參考。

def set_missing_ages(df):

    # 把已有的數值型特徵取出來丟進Random Forest Regressor中
    age_df = df[['Age','Fare', 'Parch', 'SibSp', 'Pclass']]

    # 乘客分成已知年齡和未知年齡兩部分
    known_age = age_df[age_df.Age.notnull()].as_matrix()
    unknown_age = age_df[age_df.Age.isnull()].as_matrix()

    # y即目標年齡
    y = known_age[:, 0]

    # X即特徵屬性值
    X = known_age[:, 1:]

    # fit到RandomForestRegressor之中
    rfr = RandomForestRegressor(random_state=0, n_estimators=2000, n_jobs=-1)
    rfr.fit(X, y)

    # 用得到的模型進行未知年齡結果預測
    predictedAges = rfr.predict(unknown_age[:, 1:])
#     print predictedAges
    # 用得到的預測結果填補原缺失資料
    df.loc[ (df.Age.isnull()), 'Age' ] = predictedAges 

    return df, rfr
  • 虛擬變數

虛擬變數其實就是缺失值的一種衍生變數。具體做法是通過判斷特徵值是否有缺失值來定義一個新的二分類變數。比如,特徵為A含有缺失值,我們衍生出一個新的特徵B,如果A中特徵值有缺失,那麼相應的B中的值為1,如果A中特徵值沒有缺失,那麼相應的B中的值為0。

下面是**知識星球專案(一)**中的一段程式:

data_train['CabinCat'] = data_train['Cabin'].copy()
data_train.loc[ (data_train.CabinCat.notnull()), 'CabinCat' ] = "No"
data_train.loc[ (data_train.CabinCat.isnull()), 'CabinCat' ] = "Yes"

fig, ax = plt.subplots(figsize=(10,5))
sns.countplot(x='CabinCat', hue='Survived',data=data_train)
plt.show()

下面可以通過一行程式碼清楚看到衍生的虛擬變數。

data_train[['Cabin','CabinCat']].head(10)

3. 不處理

補齊處理只是將未知值補以我們的主觀估計值,不一定完全符合客觀事實,在對不完備資訊進行補齊處理的同時,我們或多或少地改變了原始的資訊系統。而且,對空值不正確的填充往往將新的噪聲引入資料中,使挖掘任務產生錯誤的結果。因此,在許多情況下,我們還是希望在保持原始資訊不發生變化的前提下對資訊系統進行處理。

在實際應用中,一些模型無法應對具有缺失值的資料,因此要對缺失值進行處理。然而還有一些模型本身就可以應對具有缺失值的資料,此時無需對資料進行處理,比如Xgboostrfr等高階模型。

4 總結

總而言之,大部分資料探勘的預處理都會使用比較方便的方法來處理缺失值,比如均值法,但是效果上並一定好,因此還是需要根據不同的需要選擇合適的方法,並沒有一個解決所有問題的萬能方法。具體的方法採用還需要考慮多個方面的:

  • 資料缺失的原因;
  • 資料缺失值型別;
  • 樣本的資料量;
  • 資料缺失值隨機性等;

關於資料缺失值得思維導圖:

如果大家有任何好的其他方法,歡迎補充。

參考:

http://www.restore.ac.uk/PEAS/imputation.php
https://blog.csdn.net/lujiandong1/article/details/52654703
http://blog.sina.com.cn/s/blog_4b0f1da60101d8yb.html
https://www.cnblogs.com/Acceptyly/p/3985687.html

原創不易,來波點贊支援。

本篇首發於我的原創公眾號:Python資料科學,歡迎關注。
資料科學網站:datadeepin

相關文章