機器學習 第3篇:資料預處理(使用插補法處理缺失值)

悅光陰發表於2020-12-28

插補法可以在一定程度上減少偏差,常用的插補法是熱卡插補、擬合插補和多重插補。擬合插補,要求變數間存在強的相關性;多重插補(MCMC法),是在高缺失率下的首選插補方法,優點是考慮了缺失值的不確定性。

一,熱卡插補

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

二,擬合插補

擬合插補法則是利用有監督的機器學習方法,比如迴歸、最鄰近、隨機森林、支援向量機等模型,對缺失值作預測,其優勢在於預測的準確性高,缺點是需要大量的計算,導致缺失值的處理速度大打折扣。雖然替換法思想簡單、效率高效,但是其替換的值往往不具有很高的準確性,於是出現了插補方法。

1,迴歸插補

基於完整的資料集,建立迴歸方程。對於包含空值的物件,將已知屬性值代入方程來估計未知屬性值,以此估計值來進行填充。當變數不是線性相關時會導致有偏差的估計。缺失值是連續的,即定量的型別,才可以使用迴歸來預測。

2,最鄰近填充

利用knn演算法填充,其實是把目標列當做目標標量,利用非缺失的資料進行knn演算法擬合,最後對目標列缺失進行預測。(對於連續特徵一般是加權平均,對於離散特徵一般是加權投票)

from sklearn.neighbors import KNeighborsClassifier, KNeighborsRegressor

def knn_filled_func(x_train, y_train, test, k = 3, dispersed = True):
    # params: x_train 為目標列不含缺失值的資料(不包括目標列)
    # params: y_train 為不含缺失值的目標列
    # params: test 為目標列為缺失值的資料(不包括目標列)
    if dispersed:
        knn= KNeighborsClassifier(n_neighbors = k, weights = "distance")
    else:
        knn= KNeighborsRegressor(n_neighbors = k, weights = "distance")
    
    knn.fit(x_train, y_train)

3,隨機森林插補

隨機森林演算法填充的思想和knn填充是類似的,即利用已有資料擬合模型,對缺失變數進行預測。

from sklearn.ensemble import RandomForestRegressor, RandomForestClassifier

def knn_filled_func(x_train, y_train, test, k = 3, dispersed = True):
    # params: x_train 為目標列不含缺失值的資料(不包括目標列)
    # params: y_train 為不含缺失值的目標列
    # params: test 為目標列為缺失值的資料(不包括目標列)
    if dispersed:
        rf= RandomForestRegressor()
    else:
        rf= RandomForestClassifier()
    
    rf.fit(x_train, y_train)
    return test.index, rf.predict(test)

三,多重插補

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

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

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

1,多重插補的實現

多重插補是一種基於重複模擬的用於處理缺失值的方法,它從一個包含缺失值的資料集中生成一組資料完整的資料集(即不包含缺失值的資料集,通常是3-10個)。每個完整資料集都是通過對原始資料中的缺失資料進行插補而生成的。在每個完整的資料集上引用標準的統計方法,最後,把這些單獨的分析結果整合為一組結果。

多重插補法大致分為三步:

  • 為每個空值產生一套可能的插補值,這些值反映了模型的不確定性;每個值都被用來插補資料集中的缺失值,產生若干個完整資料集合。
  • 每個插補資料集合都用針對完整資料集的統計方法進行統計分析。
  • 對來自各個插補資料集的結果進行整合,產生最終的統計推斷,這一推斷考慮到了由於資料插補而產生的不確定性。該方法將空缺值視為隨機樣本,這樣計算出來的統計推斷可能受到空缺值的不確定性的影響。

2,MICE簡介

通過鏈式方程進行的多元插補(MICE,Multiple Imputation by Chained Equations),與單個插補(例如均值)相比,建立多個插補可解決缺失值的不確定性。MICE假定缺失的資料是隨機(MAR)的,這意味著,一個值丟失概率上觀測值僅取決於並且可以使用它們來預測。通過為每個變數指定插補模型,可以按變數插補資料。

例如:假設我們有X1,X2….Xk變數。如果X1缺少值,那麼它將在其他變數X2到Xk上回歸。然後,將X1中的缺失值替換為獲得的預測值。同樣,如果X2缺少值,則X1,X3至Xk變數將在預測模型中用作自變數。稍後,缺失值將被替換為預測值。

預設情況下,線性迴歸用於預測連續缺失值。Logistic迴歸用於分類缺失值。一旦完成此迴圈,就會生成多個資料集。這些資料集僅在估算的缺失值上有所不同。通常,將這些資料集分別構建模型並組合其結果被認為是一個好習慣。

本文使用R語言中的mice包來執行這些操作,首先我們來看mice包的操作思路:

首先,mice()函式從一個包含缺失資料的資料框開始,返回一個包含多個(預設為5個)完整資料集的物件。每個完整資料集都是通過對原始資料框中的缺失資料進行插補而生成的。由於插補有隨機的成分,因此每個完整資料集都略有不同。

然後,with()函式可依次對每個完整的資料集應用統計模型(如線性模型或廣義線性模型)。

最後,pool()函式把這些單獨的分析結果整合為一組結果。

最終模型的標準差和p值都準確地反映出由於缺失值和多重插補而產生的不確定性。

缺失值的插補法通過Gibbs抽樣完成,每個包含缺失值的變數都預設可通過資料集中的其他變數預測的來,於是這些預測方程便可用於預測資料的有效值。該方程不斷迭代直到所有的缺失值都收斂為止。預設情況下,預測的均值用於替換連續性變數中的缺失資料。

3,基於R的mice包的分析過程

library(mice)
imp <- mice(data, m)
fit <- with(imp, analysis)
pooled <- pool(fit)
summary(pooled)

data:包含缺失值的矩陣或資料框;
imp:一個包含m個插補資料集的列表物件,同時還含有完成插補過程的資訊。預設為5。
analysis:用來設定應用於m個插補資料集的統計分析方法。比如線性迴歸模型的lm()函式,舉個例子lm(Dream ~ Span + Gest),表示式在函式的括號中,~左邊是因變數,右邊是自變數,用+符號分隔開。這個例子中Dream是因變數,Span和Gest是自變數;
fit:一個包含m個單獨統計分析結果的列表物件;
pooled:一個包含m個統計分析平均結果的列表物件。

四,Python的MICE演算法

在處理缺失值時,可以通過鏈式方程的多重插補估算缺失值:

 鏈式方程的多重插補,也稱為“完全條件規範”,其定義如下:

從技術上講,任何能夠推理的預測模型都可以用於MICE。 在本文中,我們使用miceforest Python庫估算了資料集,該庫使用隨機森林。 出於以下幾個原因,隨機森林可與MICE演算法配合使用:

  • 不需要太多的超引數調整
  • 輕鬆處理資料中的非線性關係
  • 可以廉價地返回OOB效能
  • 幾乎可以並行化
  • 可以返回功能重要性以進行診斷

 

 程式碼如下:

 

import miceforest as mf
from sklearn.datasets import load_iris
import pandas as pd

# Load and format data
iris = pd.concat(load_iris(as_frame=True,return_X_y=True),axis=1)
iris.rename(columns = {'target':'species'}, inplace = True)
iris['species'] = iris['species'].astype('category')

# Introduce missing values
iris_amp = mf.ampute_data(iris,perc=0.25,random_state=1991)

# Create kernels. 
kernel = mf.MultipleImputedKernel(
  data=iris_amp,
  save_all_iterations=True,
  random_state=1991
)

# Run the MICE algorithm for 3 iterations on each of the datasets
kernel.mice(3,verbose=True)


kernel.plot_correlations(wspace=0.4,hspace=0.5)

# Our new dataset
new_data = iris_amp.iloc[range(50)]# Make a multiple imputed dataset with our new data
new_data_imputed = kernel.impute_new_data(new_data)# Return a completed dataset
new_completed_data = new_data_imputed.complete_data(0)


new_data_imputed.plot_imputed_distributions(wspace=0.35,hspace=0.4)

from sklearn.linear_model import LinearRegression
# For each imputed dataset, train a linear regression
# on 'sepal length (cm)'
intercepts = []
target = 'sepal length (cm)'
for d in range(kernel.dataset_count()):
    comp_dat = kernel.complete_data(d)
    comp_dat = pd.get_dummies(comp_dat)
    X, y = comp_dat.drop(target,1), comp_dat[target]
    model = LinearRegression()
    model.fit(X,y)
    intercepts.append(model.intercept_)# Load packages for plotting
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

# Make plot.
x_axis = np.arange(1.93, 2.01, 0.0001)
avg_intercept = round(np.mean(intercepts),2)
var_intercept = round(np.var(intercepts),4)
plt.plot(
    x_axis,
    norm.pdf(x_axis,avg_intercept,var_intercept)
)
plt.title(f"""
    Assumed Distribution of Intercept Term
    n=5, mean = {avg_intercept}, variance = {var_intercept}
    """
)

 

 

參考文件:

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

資料分析——缺失值處理詳解(理論篇)

處理缺失值之多重插補(Multiple Imputation)

Multiple Imputation with Random Forests in Python

miceforest 2.0.3

相關文章