scikit-learn 缺失值插補(bǔ)

2023-02-20 14:37 更新

由于各種原因,現(xiàn)實(shí)世界的許多數(shù)據(jù)集包含缺失值,通常將其編碼為空白,NaN或其他占位符。但是,此類(lèi)數(shù)據(jù)集與scikit-learn估計(jì)器不兼容,后者假定數(shù)組中的所有值都是具有含義的數(shù)字。使用不完整數(shù)據(jù)集的基本策略是舍棄包含缺失值的整行或整列。但是,這是以丟失可能有價(jià)值的數(shù)據(jù)為代價(jià)的(即使數(shù)據(jù)不完整)。更好的策略是估算缺失值,即從數(shù)據(jù)的已知部分推斷出缺失值。有關(guān)插補(bǔ),請(qǐng)參見(jiàn) 常用術(shù)語(yǔ)表和API元素條目。

6.4.1 單變量與多變量插補(bǔ)

一類(lèi)是單變量的插補(bǔ)算法,它僅使用第i個(gè)特征維度中的非缺失值(例如impute.SimpleImputer)插補(bǔ)第i個(gè)特征維度中的值。相比之下,多元插補(bǔ)算法使用整個(gè)可用特征維度集來(lái)估計(jì)缺失值(例如impute.IterativeImputer)。

6.4.2 單變量插補(bǔ)

SimpleImputer類(lèi)提供了插補(bǔ)缺失值的基本策略??梢允褂锰峁┑某A炕蚴褂萌笔е邓诟髁械慕y(tǒng)計(jì)量(平均值,中位數(shù)或眾數(shù))來(lái)估算缺失值。此類(lèi)還支持不同的缺失值編碼。

以下代碼段演示了如何使用包含缺失值的列(axis 0)的平均值替換編碼為np.nan的缺失值:

>>> import numpy as np
>>> from sklearn.impute import SimpleImputer
>>> imp = SimpleImputer(missing_values=np.nan, strategy='mean')
>>> imp.fit([[1, 2], [np.nan, 3], [7, 6]])
SimpleImputer()
>>> X = [[np.nan, 2], [6, np.nan], [7, 6]]
>>> print(imp.transform(X))
[[4.          2.        ]
 [6.          3.666...]
 [7.          6.        ]]

SimpleImputer類(lèi)還支持稀疏矩陣:

>>> import scipy.sparse as sp
>>> X = sp.csc_matrix([[1, 2], [0, -1], [8, 4]])
>>> imp = SimpleImputer(missing_values=-1, strategy='mean')
>>> imp.fit(X)
SimpleImputer(missing_values=-1)
>>> X_test = sp.csc_matrix([[-1, 2], [6, -1], [7, 6]])
>>> print(imp.transform(X_test).toarray())
[[3. 2.]
 [6. 3.]
 [7. 6.]]

請(qǐng)注意,這種格式并不打算用于在矩陣中隱式存儲(chǔ)丟失的值,因?yàn)樗鼘⒃谵D(zhuǎn)換時(shí)將其壓縮。由0編碼的缺失值必須與密集輸入配合使用。

請(qǐng)注意,這種格式并不意味著在矩陣中隱式儲(chǔ)存缺失值,因?yàn)樗鼤?huì)在轉(zhuǎn)換時(shí)將其密集化。編碼為0的缺失值必須與密集輸入一起使用。

當(dāng)strategy參數(shù)設(shè)置為'most_frequent''constant'時(shí),SimpleImputer類(lèi)還支持以字符串或者pandas表示的分類(lèi)數(shù)據(jù)。

>>> import pandas as pd
>>> df = pd.DataFrame([["a", "x"],
...                    [np.nan, "y"],
...                    ["a", np.nan],
...                    ["b", "y"]], dtype="category")
...
>>> imp = SimpleImputer(strategy="most_frequent")
>>> print(imp.fit_transform(df))
[['a' 'x']
 ['a' 'y']
 ['a' 'y']
 ['b' 'y']]

6.4.3 多變量插補(bǔ)

一種更復(fù)雜的方法是使用IterativeImputer類(lèi),該類(lèi)將每個(gè)包含缺失值的特征建模為其他特征的函數(shù),并將該估計(jì)值用于插補(bǔ)。它以迭代的方式進(jìn)行:在每個(gè)步驟中,將一個(gè)特征列指定為輸出y,而將其他特征列視為輸入X?;貧w器通過(guò)fit (X, y) 得到 y。然后,使用該回歸器預(yù)測(cè) y的缺失值。針對(duì)每個(gè)特征以迭代方式完成此操作,然后在max_iter 個(gè)插補(bǔ)回合中重復(fù)此操作。返回最后一輪估算的結(jié)果。

注意

此估算器目前仍處于試驗(yàn)階段:在沒(méi)有任何棄用周期的情況下默認(rèn)參數(shù)或行為細(xì)節(jié)可能會(huì)更改。解決以下問(wèn)題將有助于穩(wěn)定IterativeImputer:收斂標(biāo)準(zhǔn)(#14338),默認(rèn)估計(jì)量(#13286)和使用隨機(jī)狀態(tài)(#15611)。要使用它,您需要顯式導(dǎo)入enable_iterative_imputer。

>>> import numpy as np
>>> from sklearn.experimental import enable_iterative_imputer
>>> from sklearn.impute import IterativeImputer
>>> imp = IterativeImputer(max_iter=10, random_state=0)
>>> imp.fit([[1, 2], [3, 6], [4, 8], [np.nan, 3], [7, np.nan]])
IterativeImputer(random_state=0)
>>> X_test = [[np.nan, 2], [6, np.nan], [np.nan, 6]]
>>> # the model learns that the second feature is double the first
>>> print(np.round(imp.transform(X_test)))
[[ 1.  2.]
 [ 6. 12.]
 [ 3.  6.]]

SimpleImputerIterativeImputer均可以在管道中使用以構(gòu)建支持插補(bǔ)的復(fù)合估計(jì)器。在構(gòu)建估算器之前,請(qǐng)參見(jiàn)估算缺失值。

6.4.3.1 IterativeImputer的靈活性

R數(shù)據(jù)科學(xué)生態(tài)系統(tǒng)中有許多完善的插補(bǔ)包:Amelia,mi,mice,missForest等。missForest很流行,它是不同順序插補(bǔ)算法的特定實(shí)例,這些算法都可以通過(guò)IterativeImputer實(shí)現(xiàn),傳遞不同的回歸變量中用于預(yù)測(cè)缺失的特征值。對(duì)于missForest,此回歸器是隨機(jī)森林。請(qǐng)參見(jiàn)使用IterativeImputer的變體插補(bǔ)缺失值。

6.4.3.2 多重插補(bǔ)與單一插補(bǔ)

在統(tǒng)計(jì)界,通常的做法是執(zhí)行多個(gè)插補(bǔ),例如為單個(gè)特征矩陣生成m個(gè)獨(dú)立的插補(bǔ)。然后,將m個(gè)插補(bǔ)中的每一個(gè)都會(huì)進(jìn)入后續(xù)的分析管道(例如,特征工程,聚類(lèi),回歸,分類(lèi))。最終的m個(gè)分析結(jié)果(例如保留的驗(yàn)證錯(cuò)誤),使數(shù)據(jù)科學(xué)家能夠了解由于缺失值所引起的固有不確定性,分析結(jié)果可能會(huì)有所不同。上述做法稱(chēng)為多重插補(bǔ)。

IterativeImputer的實(shí)現(xiàn)受到R MICE程序包的啟發(fā)(通過(guò)鏈?zhǔn)椒匠踢M(jìn)行的多元插補(bǔ))[1],但與之不同之處在于,這里返回了一個(gè)插補(bǔ)而不是多個(gè)插補(bǔ)。但是,當(dāng) sample_posterior=True時(shí),IterativeImputer通過(guò)重復(fù)應(yīng)用于具有不同隨機(jī)種子的同一數(shù)據(jù)集, 也可以用于多個(gè)插補(bǔ)。有關(guān)多重插補(bǔ)和單一插補(bǔ)的更多討論,請(qǐng)參見(jiàn)[2]第4章。

當(dāng)用戶對(duì)由于缺失值而引起的不確定性不感興趣時(shí),在預(yù)測(cè)和分類(lèi)中單一插補(bǔ)與多重插補(bǔ)的用處仍是一個(gè)尚待解決的問(wèn)題。

請(qǐng)注意,不允許調(diào)用IterativeImputertransform方法更改樣本數(shù)。因此,無(wú)法通過(guò)單次調(diào)用來(lái)實(shí)現(xiàn)多個(gè)插補(bǔ)。

6.4.4 參考

[1]Stef van Buuren,Karin Groothuis-Oudshoorn(2011)?!癿ice:R中的鏈?zhǔn)椒匠踢M(jìn)行的多元?dú)w因”。統(tǒng)計(jì)軟件雜志45:1-67。

[2]Roderick JA Little和Donald B Rubin(1986)?!叭笔?shù)據(jù)的統(tǒng)計(jì)分析”。John Wiley & Sons, Inc., New York, NY, USA.

6.4.5 最近鄰歸因

KNNImputer類(lèi)提供插補(bǔ)使用k-最近鄰方法進(jìn)行缺失值填充。默認(rèn)情況下,支持缺失值nan_euclidean_distances的歐幾里得距離度量標(biāo)準(zhǔn)用于查找最近的鄰居。使用n_neighbors最近鄰中具有該特征值的值來(lái)估算每個(gè)特征的缺失值。鄰居的特征被平均或通過(guò)距離加權(quán)到每個(gè)鄰居。如果一個(gè)樣本缺少多個(gè)特征,則該樣本的鄰居可能會(huì)有所不同,具體取決于要插補(bǔ)的特定特征。當(dāng)可用的鄰居數(shù)量小于 n_neighbors并且與訓(xùn)練集之間沒(méi)有定義距離時(shí),在插補(bǔ)期間會(huì)使用該特征的訓(xùn)練集平均值。如果至少有一個(gè)鄰居具有定義的距離,則在插補(bǔ)期間將使用剩余鄰居的加權(quán)或未加權(quán)平均值。如果訓(xùn)練中始終缺少某個(gè)特征,則會(huì)在變換期間將其刪除transform。有關(guān)該方法的更多信息,請(qǐng)參見(jiàn)參考資料[OL2001]。

以下代碼段演示了如何使用具有缺失值的樣本的兩個(gè)最近鄰居的平均特征值替換編碼為np.nan的缺失值:

>>> import numpy as np
>>> from sklearn.impute import KNNImputer
>>> nan = np.nan
>>> X = [[1, 2, nan], [3, 4, 3], [nan, 6, 5], [8, 8, 7]]
>>> imputer = KNNImputer(n_neighbors=2, weights="uniform")
>>> imputer.fit_transform(X)
array([[1. , 2. , 4. ],
       [3. , 4. , 3. ],
       [5.5, 6. , 5. ],
       [8. , 8. , 7. ]])

[OL2001]Olga Troyanskaya, Michael Cantor, Gavin Sherlock, Pat Brown, Trevor Hastie, Robert Tibshirani, David Botstein and Russ B. Altman, Missing value estimation methods for DNA microarrays, BIOINFORMATICS Vol. 17 no. 6, 2001 Pages 520-525.

6.4.6 標(biāo)記估算值

MissingIndicator轉(zhuǎn)換器是用于將數(shù)據(jù)集轉(zhuǎn)換成相應(yīng)二進(jìn)制矩陣,以指示數(shù)據(jù)集中是否存在缺失值。此轉(zhuǎn)換與插補(bǔ)結(jié)合使用非常有用。使用插補(bǔ)時(shí),保留缺失值的信息可能會(huì)提供豐富的參考信息。請(qǐng)注意,SimpleImputerIterativeImputer都具有布爾參數(shù)add_indicator (默認(rèn)情況下為False),將其設(shè)置為時(shí)True,將提供一種方便的方式來(lái)將MissingIndicator轉(zhuǎn)換器的輸出與imputer的輸出堆疊在一起。

NaN通常用作缺失值的占位符。但是,它強(qiáng)制數(shù)據(jù)類(lèi)型為浮點(diǎn)型。參數(shù)missing_values允許指定其他占位符,例如整數(shù)。在以下示例中,我們將使用-1作為缺失值:

>>> from sklearn.impute import MissingIndicator
>>> X = np.array([[-1, -1, 1, 3],
...               [4, -1, 0, -1],
...               [8, -1, 1, 0]])
>>> indicator = MissingIndicator(missing_values=-1)
>>> mask_missing_values_only = indicator.fit_transform(X)
>>> mask_missing_values_only
array([[ True,  True, False],
       [False,  True,  True],
       [False,  True, False]])

features參數(shù)用于選擇為其構(gòu)建掩碼的特征。默認(rèn)情況下,該參數(shù)的取值是'missing-only',在fit時(shí)返回包含缺失值的特征的掩碼:

>>> indicator.features_
array([0, 1, 3])

features參數(shù)可以設(shè)置為'all'返回所有特征,無(wú)論它們是否包含缺失值:

>>> indicator = MissingIndicator(missing_values=-1, features="all")
>>> mask_all = indicator.fit_transform(X)
>>> mask_all
array([[ True,  True, False, False],
       [False,  True, False,  True],
       [False,  True, False, False]])
>>> indicator.features_
array([0, 1, 2, 3])

Pipeline中使用MissingIndicator時(shí),一定要使用FeatureUnionColumnTransformer添加指示特征到常規(guī)特征中。首先,我們獲得iris數(shù)據(jù)集,并向其中添加一些缺失值。

>>> from sklearn.datasets import load_iris
>>> from sklearn.impute import SimpleImputer, MissingIndicator
>>> from sklearn.model_selection import train_test_split
>>> from sklearn.pipeline import FeatureUnion, make_pipeline
>>> from sklearn.tree import DecisionTreeClassifier
>>> X, y = load_iris(return_X_y=True)
>>> mask = np.random.randint(0, 2, size=X.shape).astype(np.bool)
>>> X[mask] = np.nan
>>> X_train, X_test, y_train, _ = train_test_split(X, y, test_size=100,
...                                                random_state=0)

現(xiàn)在我們創(chuàng)建一個(gè)FeatureUnion。為了分類(lèi)器可以處理此數(shù)據(jù),所有特征都將使用 SimpleImputer進(jìn)行估算。此外,它還會(huì)從中 MissingIndicator添加指標(biāo)變量。

>>> transformer = FeatureUnion(
...     transformer_list=[
...         ('features', SimpleImputer(strategy='mean')),
...         ('indicators', MissingIndicator())])
>>> transformer = transformer.fit(X_train, y_train)
>>> results = transformer.transform(X_test)
>>> results.shape
(100, 8)

當(dāng)然,我們不能使用轉(zhuǎn)換器進(jìn)行任何預(yù)測(cè)。我們應(yīng)該使用分類(lèi)器將其包裝在Pipeline中(例如DecisionTreeClassifier), 以便能夠進(jìn)行預(yù)測(cè)。

>>> clf = make_pipeline(transformer, DecisionTreeClassifier())
>>> clf = clf.fit(X_train, y_train)
>>> results = clf.predict(X_test)
>>> results.shape
(100,)


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)