作者:Jason Brownlee
翻譯:吳振東
校對:馮羽
本文約4500字,建議閱讀10分鐘
本文以病馬數據集爲例,幫助你瞭解在機器學習領域如何利用統計策略來處理缺失值,對代碼進行了較爲詳細的講解。
數據有可能會含有缺失值,而這可能會導致多種機器學習算法出現問題。
同樣地,在你對自己的預測任務進行建模之前,對數據每一列進行缺失值識別和替換是非常恰當的做法。這一步驟被稱爲數據缺失值插補處理,或者簡稱插補。
一種常見的數據缺失值插補方式是計算每一列的統計值(例如均值),並用這個值來替換該列所有的缺失值。這是一個非常受歡迎的方法,因爲對於訓練集很容易計算出一列的統計值,另外這種方法所獲得的效果也比較理想。
在這篇教程中,你會了解到在機器學習領域如何利用統計插補來處理缺失值。
在學完這篇教程後,你將會掌握:
缺失值必須被標記爲NaN,且可以被用統計方法計算出的列的值來替代。
如何載入一份帶有缺失值的CSV文件,用NaN來標識缺失值,展現出每一列的缺失值數量和百分比。
如何用統計量來填充缺失值,作爲數據準備方法中的一個步驟,用於評估模型和用最終模型和新數據來擬合預測結果。
讓我們開始吧!
教程綜述
這篇教程可以被劃分爲三部分,分別是:
1. 統計插補
2. 病馬(Horse Colic)數據集
3. 利用SimpleImputer來進行統計插補
1. SimpleImputer 數據轉換
2. SimpleImputer 和模型評估
3. 比較不同的插補統計量
4. 在預測時進行SimpleImputer轉換
統計插補
一個數據集有可能含有缺失值。
缺失值是指一行數據中某一項或者多項的數據值沒有意義。有可能是值爲空,也有可能是用一些特殊的字符或值來表示,例如問號“?”。
“這些值有可能以多種形式來表示。我見過的形式比如有完全爲空,一個空的字符串,“NULL”字段、“N/A”或“NaN”,再就是數字0等等。不管它們以什麼樣的形式出現在你的數據集裏,你要清楚自己的目的,並且確定這些數據的作用匹配達到你的預期,這樣將會使你在開始使用數據時減少問題的存在。”
——源自2012年出版的《Bad Data Handbook》,第10頁
書籍地址:https://amzn.to/3b5yutA
數據有可能因多種原因而造成缺失,這需要根據問題所在的具體領域來確定,比如說包括測量儀器的損壞或者說數據本身就無法獲得。
“數據缺失的出現可能處於多種理由,比如說測量儀器出現故障,在數據收集階段變更了實驗設計,或者從多組近似卻不相同的數據集中所進行採集。”
——源自2016年出版的《Data Mining: Practical Machine Learning Tools and Techniques》,第63頁
書籍地址:https://amzn.to/3bbfIAP
大部分機器學習算法都要求輸入的類型爲數值,而且數據集中的每一行每一列都有值的存在。既然如此,缺失值的出現可能會導致機器學習算法產生問題。
像這樣,普遍情況是識別出數據集中的缺失值,然後用數值來替代它。這個過程被稱爲數據插補或數據缺失值插補。
一種簡單且流行的數據插補是使用統計方法,對缺失值所在這一列估算出一個值,然後用這個統計方法計算出的統計量來代替這列中的缺失值。
這種方法很簡單,因爲可以很快計算出統計量,另外這也是很受歡迎的,因爲這種方法被證明是非常有效的。
常見的統計量包括:
整列的平均值
整列的中位數值
整列的衆數值
一個常數值
現在我們對這些用來缺失值插補的統計方法已經有所熟悉,接下來一起來看看帶有缺失值的數據。
病馬數據集
病馬數據集用來記錄患有腹絞痛的馬匹所出現的醫學特徵,還有馬匹最終是生存下來還是死亡。
該數據集共有300條記錄(300行),有26個輸入變量和1個輸出變量。這是一個二分類預測問題,標籤有兩個值,馬匹活着是1,馬匹死亡則是2。
一個簡單的模型所能達到的分類準確率約爲67%。一個表現極佳的模型,使用3次重複的10折交叉驗證的話,預測結果的準確率能夠達到85.2%。這定義了針對該數據集建模所能表現的預期範圍。
這個數據集在多個列上包含大量的缺失值,每一個缺失值都標有問號“?”。
下面是該數據集中幾條含有缺失值的樣本示例:
2,1,530101,38.50,66,28,3,3,?,2,5,4,4,?,?,?,3,5,45.00,8.40,?,?,2,2,11300,00000,00000,2
1,1,534817,39.2,88,20,?,?,4,1,3,4,2,?,?,?,4,2,50,85,2,2,3,2,02208,00000,00000,2
2,1,530334,38.30,40,24,1,1,3,1,3,3,1,?,?,?,1,1,33.00,6.70,?,?,1,2,00000,00000,00000,1
1,9,5290409,39.10,164,84,4,1,6,2,2,4,4,1,2,5.00,3,?,48.00,7.20,3,5.30,2,1,02208,00000,00000,1
...
你可以在下面的鏈接瞭解更多關於該數據集的訊息:
Horse Colic Dataset
https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv
Horse Colic Dataset Description
https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.names
我們不需要下載該數據集,因爲在接下來的代碼樣例中會自動下載。
在使用Python語言來載入數據集時,將所有的缺失值標記爲NaN(而不是使用一個數字),是一種很好的做法。
我們可以使用Pandas庫中的read_csv()函數來加載數據集,然後指定“na_values”來載入用“?”符號表示的缺失值。
...
# load dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv'
dataframe = read_csv(url, header=None, na_values='?')
在加載之後,我們來檢查一下數據,確定所有的“?”都已被標註爲NaN。
我們可以列舉出數據集的每一列,觀察下每一列缺失值的數量。
...
# summarize the number of rows with missing values for each column
for i in range(dataframe.shape[1]):
# count number of rows with missing values
n_miss = dataframe[[i]].isnull().sum()
perc = n_miss / dataframe.shape[0] * 100
print('> %d, Missing: %d (%.1f%%)' % (i, n_miss, perc))
試試下面這一整段的代碼,用來完成數據樣本的載入,並對整個數據集進行概述。
# summarize the horse colic dataset
from pandas import read_csv
# load dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv'
dataframe = read_csv(url, header=None, na_values='?')
# summarize the first few rows
print(dataframe.head())
# summarize the number of rows with missing values for each column
for i in range(dataframe.shape[1]):
# count number of rows with missing values
n_miss = dataframe[[i]].isnull().sum()
perc = n_miss / dataframe.shape[0] * 100
print('> %d, Missing: %d (%.1f%%)' % (i, n_miss, perc))
執行這段樣例代碼,載入數據集,查看前五行的內容。
我們可以看到所有標記爲“?”的缺失值已經被NaN所替代。
0 1 2 3 4 5 6 ... 21 22 23 24 25 26 27
0 2.0 1 530101 38.5 66.0 28.0 3.0 ... NaN 2.0 2 11300 0 0 2
1 1.0 1 534817 39.2 88.0 20.0 NaN ... 2.0 3.0 2 2208 0 0 2
2 2.0 1 530334 38.3 40.0 24.0 1.0 ... NaN 1.0 2 0 0 0 1
3 1.0 9 5290409 39.1 164.0 84.0 4.0 ... 5.3 2.0 1 2208 0 0 1
4 2.0 1 530255 37.3 104.0 35.0 NaN ... NaN 2.0 2 4300 0 0 2
[5 rows x 28 columns]
接下來,我們可以看一下關於數據所有列的清單,以及每一列中缺失值的數量和所佔的百分比。
我們可以看到有些列(比如說第1和第2列)沒有缺失值,而有些列(比如說第15和第21列)有很多缺失值,甚至是缺失值佔了大多數。
> 0, Missing: 1 (0.3%)
> 1, Missing: 0 (0.0%)
> 2, Missing: 0 (0.0%)
> 3, Missing: 60 (20.0%)
> 4, Missing: 24 (8.0%)
> 5, Missing: 58 (19.3%)
> 6, Missing: 56 (18.7%)
> 7, Missing: 69 (23.0%)
> 8, Missing: 47 (15.7%)
> 9, Missing: 32 (10.7%)
> 10, Missing: 55 (18.3%)
> 11, Missing: 44 (14.7%)
> 12, Missing: 56 (18.7%)
> 13, Missing: 104 (34.7%)
> 14, Missing: 106 (35.3%)
> 15, Missing: 247 (82.3%)
> 16, Missing: 102 (34.0%)
> 17, Missing: 118 (39.3%)
> 18, Missing: 29 (9.7%)
> 19, Missing: 33 (11.0%)
> 20, Missing: 165 (55.0%)
> 21, Missing: 198 (66.0%)
> 22, Missing: 1 (0.3%)
> 23, Missing: 0 (0.0%)
> 24, Missing: 0 (0.0%)
> 25, Missing: 0 (0.0%)
> 26, Missing: 0 (0.0%)
> 27, Missing: 0 (0.0%)
現在我們知道了病馬數據集中含有缺失值,接下來看看如何使用統計插補。
用SimpleImputer來進行統計插補
scikit-learn機器學習工具庫提供SimpleImputer類來支持數據缺失值插補。
在這一章節,我們會探索如何高效地使用SimpleImputer。
SimpleImputer數據轉換
SimpleImputer 是一個數據轉換工具,基於每一列所計算出的統計量類型進行初始配置,例如平均值。
...
# define imputer
imputer = SimpleImputer(strategy='mean')
接下來這個插補器會適配數據集,並計算每一列的統計量。
...
# fit on the dataset
imputer.fit(X)
這個適配的插補器會被應用於數據集上,創建一個數據集複本,複本中每一列的缺失值都已經被統計量值所替換。
...
# transform the dataset
Xtrans = imputer.transform(X)
我們可以將這一工具用在病馬數據集上,先後顯示下轉換前和轉換後的缺失值總數,從而確認這個方法是有效的。
完整的示例代碼如下:
# statistical imputation transform for the horse colic dataset
from numpy import isnan
from pandas import read_csv
from sklearn.impute import SimpleImputer
# load dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv'
dataframe = read_csv(url, header=None, na_values='?')
# split into input and output elements
data = dataframe.values
X, y = data[:, :-1], data[:, -1]
# print total missing
print('Missing: %d' % sum(isnan(X).flatten()))
# define imputer
imputer = SimpleImputer(strategy='mean')
# fit on the dataset
imputer.fit(X)
# transform the dataset
Xtrans = imputer.transform(X)
# print total missing
print('Missing: %d' % sum(isnan(Xtrans).flatten()))
執行示例代碼,首次加載數據集時缺失值的總數爲1605。
當轉換工具完成配置、適配和執行三步後,新數據集的結果中已經不再存在缺失值,這說明這個功能達到了我們的預期。
每一個缺失值都已經被所在列的平均值所替代。
Missing: 1605
Missing: 0
SimpleImputer 和模型評估
在評估機器學習模型方面,使用K折交叉驗證是一種不錯的方法。
爲了正確地使用缺失值插補方法,避免出現數據泄露的情況,這要求被計算的統計量範圍僅僅針對訓練集的每一列,然後再應用於每一折的訓練數據和測試數據上。
如果我們利用重採樣的方法來調參,或者用於評估模型的性能,缺失值插補需要包含在重採樣過程之內。
——源自2013年出版的《Applied Predictive Modeling》,第42頁
書籍地址:https://amzn.to/3b2LHTL
這可以在創造建模流水線(Pipeline)過程中完成,其中第一步就是缺失值插補,第二步纔是模型。這一過程可以利用Pipeline類。
比如說,下面的Pipeline 使用了 SimpleImputer 方法,將平均值作爲統計策略,隨後利用了隨機森林模型。
...
# define modeling pipeline
model = RandomForestClassifier()
imputer = SimpleImputer(strategy='mean')
pipeline = Pipeline(steps=[('i', imputer), ('m', model)])
針對病馬數據集,利用平均值進行缺失值插補和隨機森林模型的建模流水線,外加使用10折交叉驗證,我們可以評估下上述內容結合後所呈現的效果。
完整的示例如下:
# evaluate mean imputation and random forest for the horse colic dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.pipeline import Pipeline
# load dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv'
dataframe = read_csv(url, header=None, na_values='?')
# split into input and output elements
data = dataframe.values
X, y = data[:, :-1], data[:, -1]
# define modeling pipeline
model = RandomForestClassifier()
imputer = SimpleImputer(strategy='mean')
pipeline = Pipeline(steps=[('i', imputer), ('m', model)])
# define model evaluation
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise')
print('Mean Accuracy: %.3f (%.3f)' % (mean(scores), std(scores)))
正確地執行上面的示例,每一折交叉驗證過程中都有使用缺失值插補。
流水線用來評估三次重複10折交叉驗證的效果,得到的結果是三次分類的準確率平均值約爲84.2%,成績不錯。
Mean Accuracy: 0.842 (0.049)
比較不同的插補統計量
我們利用“均值”的統計策略來插補缺失值,這個方法對於該數據集來說是好是壞呢?
答案是,如果我們是隨意選擇一種方法的話,我們肯定無法來評價它的好壞。
我們可以設計一個實驗,檢測每種統計策略,通過比較均值、中位數、衆數(更常用)和常數(0)四種策略,得出哪種策略對這個數據集是最好的策略。每個方法的平均準確率可以用作比較的依據。
完整的示例代碼如下:
# compare statistical imputation strategies for the horse colic dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.pipeline import Pipeline
from matplotlib import pyplot
# load dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv'
dataframe = read_csv(url, header=None, na_values='?')
# split into input and output elements
data = dataframe.values
X, y = data[:, :-1], data[:, -1]
# evaluate each strategy on the dataset
results = list()
strategies = ['mean', 'median', 'most_frequent', 'constant']
for s in strategies:
# create the modeling pipeline
pipeline = Pipeline(steps=[('i', SimpleImputer(strategy=s)), ('m', RandomForestClassifier())])
# evaluate the model
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(pipeline, X, y, scoring='accuracy', cv=cv, n_jobs=-1)
# store results
results.append(scores)
print('>%s %.3f (%.3f)' % (s, mean(scores), std(scores)))
# plot model performance for comparison
pyplot.boxplot(results, labels=strategies, showmeans=True)
pyplot.xticks(rotation=45)
pyplot.show()
執行上面的示例代碼,利用重複交叉驗證方法,評估作用在病馬數據集的每種統計插補策略。
你所得到的特定結果可能會非常多樣化,這歸咎於學習算法的隨機性。你可以嘗試去多次執行這段代碼。
每一種統計策略所得到的平均準確率結果如下。結果顯示利用常數(例如0)作爲填充值的效果最佳,準確率爲86.7%,優於其他結果。
>mean 0.851 (0.053)
>median 0.844 (0.052)
>most_frequent 0.838 (0.056)
>constant 0.867 (0.044)
在執行完代碼之後,繪製一個盒須圖來表示所對應的結果集,讓我們來看一下結果的分佈,從而用對其進行比較。
我們可以清楚地看到用常數來進行缺失值填充的統計策略,所得到的準確率優於其他策略。
在病馬數據集上利用不同統計策略的缺失值填充後所得模型結果的盒須圖
在進行預測時的SimpleImputer轉換
我們可能希望創作一個最終的模型流水線,利用的是常數填充缺失值的方式,使用隨機森林算法,對新數據進行預測。
我們可以先定義一個流水線,然後在所有可用數據上進行擬合,最後用predict() 函數進行轉換,把新數據作爲一個參數。
重要的是,新數據中每一行的缺失值必須用NaN來表示。
...
# define new data
row = [2,1,530101,38.50,66,28,3,3,nan,2,5,4,4,nan,nan,nan,3,5,45.00,8.40,nan,nan,2,2,11300,00000,00000]
完整的示例代碼如下所示:
# constant imputation strategy and prediction for the hose colic dataset
from numpy import nan
from pandas import read_csv
from sklearn.ensemble import RandomForestClassifier
from sklearn.impute import SimpleImputer
from sklearn.pipeline import Pipeline
# load dataset
url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/horse-colic.csv'
dataframe = read_csv(url, header=None, na_values='?')
# split into input and output elements
data = dataframe.values
X, y = data[:, :-1], data[:, -1]
# create the modeling pipeline
pipeline = Pipeline(steps=[('i', SimpleImputer(strategy='constant')), ('m', RandomForestClassifier())])
# fit the model
pipeline.fit(X, y)
# define new data
row = [2,1,530101,38.50,66,28,3,3,nan,2,5,4,4,nan,nan,nan,3,5,45.00,8.40,nan,nan,2,2,11300,00000,00000]
# make a prediction
yhat = pipeline.predict([row])
# summarize prediction
print('Predicted Class: %d' % yhat[0])
執行上方代碼用建模流水線來擬合所有的可用數據。
針對一行帶有以NaN來標識缺失值的新數據,分類預測順利完成。
Predicted Class: 2
總結
在這篇教程中,你找到了如何使用統計策略在機器學習中進行數據缺失值填充。
明確地講,你學到了:
缺失值必須以NaN來標識,可以計算出一列數據的統計值,然用其來替換缺失值。
如何載入一份帶有缺失值的CSV文件,並用NaN來標識缺失值,展現出每一列的缺失值數量和百分比。
如何用統計策略來填充缺失值,作爲數據準備方法中的一個步驟,用於評估模型和用最終模型和新數據來擬合預測結果。
你有什麼問題嗎?
請在評論區留下你的問題,我會盡力去回答。
作者簡介
Jason Brownlee,機器學習專業博士,通過親自動手寫教程的方式來指導開發人員如何使用現代機器學習方法來取得結果。
原文標題:
Statistical Imputation for Missing Values in Machine Learning
原文鏈接:
https://machinelearningmastery.com/statistical-imputation-for-missing-values-in-machine-learning/
編輯:於騰凱
校對:林亦霖
譯者簡介
吳振東,法國洛林大學計算機與決策專業碩士。現從事人工智能和大數據相關工作,以成爲數據科學家爲終生奮鬥目標。來自山東濟南,不會開挖掘機,但寫得了Java、Python和PPT。
翻譯組招募信息
工作內容:需要一顆細緻的心,將選取好的外文文章翻譯成流暢的中文。如果你是數據科學/統計學/計算機類的留學生,或在海外從事相關工作,或對自己外語水平有信心的朋友歡迎加入翻譯小組。
你能得到:定期的翻譯培訓提高志願者的翻譯水平,提高對於數據科學前沿的認知,海外的朋友可以和國內技術應用發展保持聯繫,THU數據派產學研的背景爲志願者帶來好的發展機遇。
其他福利:來自於名企的數據科學工作者,北大清華以及海外等名校學生他們都將成爲你在翻譯小組的夥伴。
點擊文末“閱讀原文”加入數據派團隊~
轉載須知
如需轉載,請在開篇顯著位置註明作者和出處(轉自:數據派ID:DatapiTHU),並在文章結尾放置數據派醒目二維碼。有原創標識文章,請發送【文章名稱-待授權公衆號名稱及ID】至聯繫郵箱,申請白名單授權並按要求編輯。
發佈後請將鏈接反饋至聯繫郵箱(見下方)。未經許可的轉載以及改編者,我們將依法追究其法律責任。
點擊“閱讀原文”擁抱組織